pax_global_header00006660000000000000000000000064141342316030014507gustar00rootroot0000000000000052 comment=b99a808cf9955090b909c72d6a0da5295c3cbc7c yara-4.1.3/000077500000000000000000000000001413423160300124505ustar00rootroot00000000000000yara-4.1.3/.bazelrc000066400000000000000000000001131413423160300140660ustar00rootroot00000000000000# Build in C++17 mode without a custom CROSSTOOL build --cxxopt=-std=c++17 yara-4.1.3/.clang-format000066400000000000000000000104221413423160300150220ustar00rootroot00000000000000# clang-format configuration applied to all source files in this project. # Requires clang-format version 10.0.0 or newer. --- Language: Cpp BasedOnStyle: Google AccessModifierOffset: -1 AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignConsecutiveMacros: true AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: true AfterEnum: false AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false AfterStruct: true AfterUnion: false AfterExternBlock: false BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true BreakBeforeBinaryOperators: None BreakBeforeBraces: Allman BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: true DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true IncludeBlocks: Preserve IncludeCategories: - Regex: '^' Priority: 2 - Regex: '^<.*\.h>' Priority: 1 - Regex: '^<.*' Priority: 2 - Regex: '.*' Priority: 3 IncludeIsMainRegex: '([-_](test|unittest))?$' IndentCaseLabels: false IndentPPDirectives: None IndentWidth: 2 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: "^\ begin_declarations|\ begin_struct.*$" MacroBlockEnd: "^\ end_declarations|\ end_struct.*$" MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Never ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 100 PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 1000 PointerAlignment: Left RawStringFormats: - Language: Cpp Delimiters: - cc - CC - cpp - Cpp - CPP - 'c++' - 'C++' CanonicalDelimiter: '' BasedOnStyle: google - Language: TextProto Delimiters: - pb - PB - proto - PROTO EnclosingFunctions: - EqualsProto - EquivToProto - PARSE_PARTIAL_TEXT_PROTO - PARSE_TEST_PROTO - PARSE_TEXT_PROTO - ParseTextOrDie - ParseTextProtoOrDie CanonicalDelimiter: '' BasedOnStyle: google ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Auto StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 8 UseTab: Never ... yara-4.1.3/.github/000077500000000000000000000000001413423160300140105ustar00rootroot00000000000000yara-4.1.3/.github/workflows/000077500000000000000000000000001413423160300160455ustar00rootroot00000000000000yara-4.1.3/.github/workflows/main.yml000066400000000000000000000011271413423160300175150ustar00rootroot00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'yara' dry-run: false - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'yara' fuzz-seconds: 600 dry-run: false - name: Upload Crash uses: actions/upload-artifact@v1 if: failure() with: name: artifacts path: ./out/artifacts yara-4.1.3/.gitignore000066400000000000000000000016111413423160300144370ustar00rootroot00000000000000# Generic auto-generated build files *~ *.a *.gcda *.gcno *.gcov *.la *.lai *.lo *.Plo *.Po *.o *.so *.so.[0-9][0-9]* *.so.[0-9][0-9]*.[0-9][0-9]*.[0-9][0-9]* *.Tpo *.m4 *.dSYM .deps .libs INSTALL Makefile Makefile.in stamp-h1 # Specific auto-generated build files /ABOUT-NLS /aclocal.m4 /ar-lib /autom4te.cache/ /build-aux /compile /config.guess /config.h /config.h.in /config.log /config.rpath /config.status /config.sub /configure /depcomp /install-sh /libtool /ltmain.sh /missing /test-driver /ylwrap /m4 !/m4/acx_pthread.m4 # Project specific files /yara /yarac /libyara/modules/.dirstamp libyara/proc/.dirstamp libyara/yara.pc /tests/.dirstamp # Linux and Mac files *.swp .DS_Store # Files generated by tests test-* # Bazel bazel-* # Visual Studio files Release/ Debug/ windows/*/.vs x64/ *.obj *.suo *.sdf *.opendb *.opensdf *.VC.db # NuGet windows/*/packages/ *.trs *.log .dirstamp .idea/ yara-4.1.3/.travis.yml000066400000000000000000000111211413423160300145550ustar00rootroot00000000000000language: c matrix: include: # Build for 64-bit Linux without profiling enabled - os: linux dist: focal sudo: required env: CONFIGFLAGS="CFLAGS=-m64 --enable-cuckoo --enable-magic --enable-pb-tests --enable-address-sanitizer" # The certificate for scan.coverity.com is too new and is not recognized by # wget. This command adds the certificate to /etc/ssl/certs/ca-certificates.crt # See: https://github.com/travis-ci/travis-ci/issues/6142 before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt install: | sudo apt-get update sudo apt-get install -y \ autoconf \ automake \ libtool \ libjansson-dev \ libmagic-dev \ libssl-dev \ protobuf-compiler \ protobuf-c-compiler \ libprotobuf-c-dev # Build for 64-bit Linux with profiling enabled - os: linux dist: focal sudo: required env: CONFIGFLAGS="CFLAGS=-m64 --enable-cuckoo --enable-magic --enable-pb-tests -enable-address-sanitizer --enable-profiling" install: | sudo apt-get update sudo apt-get install -y \ autoconf \ automake \ libtool \ libjansson-dev \ libmagic-dev \ libssl-dev \ protobuf-compiler \ protobuf-c-compiler \ libprotobuf-c-dev # Build for 32-bit Linux - os: linux dist: trusty sudo: required env: CONFIGFLAGS="CFLAGS=-m32 --enable-cuckoo --enable-magic" install: | sudo dpkg --add-architecture i386 && sudo rm -rf /etc/apt/sources.list.d/ sudo apt-get update sudo apt-get remove postgresql-9.3 sudo apt-get upgrade -y gcc sudo apt-get install -y gcc-multilib autoconf automake libtool libjansson-dev:i386 libmagic-dev:i386 libssl-dev:i386 # Build for 64-bit Window using MinGW cross-compiler - os: linux dist: focal sudo: required env: CONFIGFLAGS=--host=x86_64-w64-mingw32 install: | sudo apt-get update sudo apt-get install -y gcc-mingw-w64 autoconf automake libtool # Build for 64-bit Window using MinGW cross-compiler - os: linux dist: focal sudo: required env: CONFIGFLAGS=--host=i686-w64-mingw32 install: | sudo apt-get update sudo apt-get install -y gcc-mingw-w64 autoconf automake libtool # Build for 64-bit Linux using Bazel instead of make. - os: linux dist: focal install: | wget https://github.com/bazelbuild/bazel/releases/download/2.1.0/bazel_2.1.0-linux-x86_64.deb sudo dpkg -i bazel_2.1.0-linux-x86_64.deb before_script: bazel info script: | bazel test @jansson//... //tests/... # Build for OS X # - os: osx # osx_image: xcode7.3 # - os: osx # osx_image: xcode8.3 # - os: osx # osx_image: xcode9.2 before_script: ./bootstrap.sh script: | rvm get head # Workaround for Travis CI issue https://github.com/travis-ci/travis-ci/issues/6307 set -e unset CC # A pre-set CC overrides --host settings. ./configure $CONFIGFLAGS make clean && make case "$CONFIGFLAGS" in *--host=*mingw*) ;; *) make check ;; esac after_failure: if [ -e test-suite.log ]; then cat test-suite.log; fi env: global: # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created # via the "travis encrypt" command using the project repo's public key - secure: "JWobvJ94pWt/xVciQURkNFS3I+gyu2IyZPKYEs6HDlHrpHs4BoVDZeRjmgx0s6aDeQjKJHowGDu17IlbnCkKzXrZErEJkA+Oc/d0SwgXKiUU9WYiaGBJjJUoYZw66QIEuGGKkF4uQ7EIcW/vN7wzrCDyAiPeOPUjVP4Tc2XRzmkSfakfmf9cE5nqT84DPUYiRegM7iepMrZi9kEaAoboBuETT+6eUKdERRadM0QNjZmCYMEMjtFj3lE51Ey2stGqZdKJvJN0FUmxGoaXCFFAsNmZPnFeDkqTf0a+MzxG2m4nnIXyNC/nT5XLItKHog4KROHb4tUpCZJ4iJhcw3loWMCtkZqB2fq2PaOkKk2zxPr3HLCn7ltmOzReBEDjEg68UqWydRW5534JGosbcA9IfshS1VqnZLgGwQHieXNeqhJUumt1DpON7AQEiEzbzAk0y2VcPlDPuCt9QS1k+zPMZLzbwgvs1ZOH39oFESW+iEDdzZjbhyC3J6azTHFcnA7r5SsYe1pzcSUaYtS1ehhb0lU/442JSHw2j00Nv9qFycYNvDrRNQNBxLziVustT0WJoVdFlkKy16iu1tUYOVXKgmMfqUDINfU6zRz3DskVuB9MZzq/4cMdK4jMRIDNZWvye1BzM7o/PiJoNaQc/6iav2RD+5YV46bBr60TqnYyjlM=" addons: apt: packages: - ca-certificates coverity_scan: project: name: "plusvic/yara" description: "Build submitted via Travis CI" notification_email: plusvic@gmail.com build_command_prepend: "./configure; make clean" build_command: "make -j 4" branch_pattern: master yara-4.1.3/AUTHORS000066400000000000000000000010631413423160300135200ustar00rootroot00000000000000# This is the official list of YARA authors for copyright purposes. # This file is distinct from the CONTRIBUTORS files. # See the latter for an explanation. # Names should be added to this file as # Name or Organization # The email address is not required for organizations. # Please keep the list sorted. Google Inc. Hilko Bengen Joachim Metz Stefan Buehlmann Victor M. Alvarez ; Wesley Shields yara-4.1.3/BUILD.bazel000066400000000000000000000103221413423160300143240ustar00rootroot00000000000000# Copyright (c) 2019. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Bazel (http://bazel.io/) WORKSPACE file for YARA. load("@com_github_virustotal_yara//:bazel/yara.bzl", "yara_library") # Just YARA's error codes. This is useful for sandboxing as it avoids pulling # in the whole library. cc_library( name = "yara_errors", hdrs = ["libyara/include/yara/error.h"], visibility = ["//visibility:public"], ) # Configuration setting for enabling profiling. Usage: # bazel build ... --define yara_profiling=enabled config_setting( name = "profiling_enabled", values = {"define": "yara_profiling=enabled"}, ) # Configuration setting for using BoringSSL instead of OpenSSL. Notice that # some features related to PE signatures in the "pe" module will be disabled # because of missing functionality in BoringSSL. Usage: # bazel build ... --define yara_crypto_library=boringssl config_setting( name = "crypto_library_boringssl", values = {"define": "yara_crypto_library=boringssl"}, ) yara_library( name = "libyara", crypto_libs = select({ ":crypto_library_boringssl": ["@boringssl//:crypto"], "//conditions:default": ["@openssl//:crypto"], }), defines = select({ ":profiling_enabled": ["YR_PROFILING_ENABLED"], "//conditions:default": [], }) + select({ ":crypto_library_boringssl": ["BORINGSSL"], "//conditions:default": [], }), modules = [ "cuckoo", "dex", "dotnet", "elf", "hash", "macho", "magic", "math", "pe", "tests", "time", ], modules_srcs = [ "libyara/modules/cuckoo/cuckoo.c", "libyara/modules/dex/dex.c", "libyara/modules/dotnet/dotnet.c", "libyara/modules/elf/elf.c", "libyara/modules/hash/hash.c", "libyara/modules/magic/magic.c", "libyara/modules/macho/macho.c", "libyara/modules/math/math.c", "libyara/modules/pe/pe.c", "libyara/modules/pe/pe_utils.c", "libyara/modules/tests/tests.c", "libyara/modules/time/time.c", ], deps = [ "@jansson", "@magic", ], ) # Code shared by the command-line tools. cc_library( name = "cli_shared", srcs = [ "cli/args.c", "cli/threading.c", ], hdrs = [ "cli/args.h", "cli/common.h", "cli/threading.h", ], deps = [":libyara"], ) # YARA command-line tool cc_binary( name = "yara", srcs = ["cli/yara.c"], visibility = ["//visibility:public"], deps = [ ":cli_shared", ":libyara", ], ) # YARA compiler cc_binary( name = "yarac", srcs = ["cli/yarac.c"], visibility = ["//visibility:public"], deps = [ ":cli_shared", ":libyara", ], ) yara-4.1.3/CONTRIBUTORS000066400000000000000000000030641413423160300143330ustar00rootroot00000000000000# This is the official list of people who can contribute # (and typically have contributed) code to the YARA repository. # The AUTHORS file lists the copyright holders; this file # lists people. For example, Google employees are listed here # but not in AUTHORS, because Google holds the copyright. # # The submission process automatically checks to make sure # that people submitting code are listed in this file (by email address). # # Names should be added to this file only after verifying that # the individual or the individual's organization has agreed to # the appropriate Contributor License Agreement, found here: # # http://code.google.com/legal/individual-cla-v1.0.html # http://code.google.com/legal/corporate-cla-v1.0.html # # The agreement for individuals can be filled out on the web. # # When adding J Random Contributor's name to this file, # either J's name or J's organization's name should be # added to the AUTHORS file, depending on whether the # individual or corporate CLA was used. # Names should be added to this file like so: # Name # Please keep the list sorted. Anthony Desnos Antonio Vargas Gonzalez Christian Blichmann Hilko Bengen Joachim Metz Karl Hiramoto Mike Wiacek Shane Huntley Stefan Buehlmann Victor M. Alvarez ; Wesley Shields yara-4.1.3/COPYING000066400000000000000000000027251413423160300135110ustar00rootroot00000000000000Copyright (c) 2007-2016. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. yara-4.1.3/Makefile.am000066400000000000000000000064331413423160300145120ustar00rootroot00000000000000AM_CFLAGS=-Wall -I$(srcdir)/libyara/include if GCOV check: @tests/gcov-summary MOSTLYCLEANFILES = {cli,tests}/*.gc{no,da,ov} AM_CFLAGS+=-O0 -g -ftest-coverage -fprofile-arcs else if DEBUG AM_CFLAGS+=-g endif if OPTIMIZATION AM_CFLAGS+=-O3 else AM_CFLAGS+=-O0 endif endif if ADDRESS_SANITIZER AM_CFLAGS+=-fsanitize=address endif # Build the library in the hand subdirectory first. SUBDIRS = libyara DIST_SUBDIRS = libyara ACLOCAL_AMFLAGS=-I m4 bin_PROGRAMS = yara yarac yara_SOURCES = \ cli/args.c \ cli/args.h \ cli/common.h \ cli/threading.c \ cli/threading.h \ cli/yara.c yara_LDADD = -Llibyara/.libs -lyara yara_DEPENDENCIES = libyara/.libs/libyara.la yarac_SOURCES = \ cli/args.c \ cli/args.h \ cli/common.h \ cli/yarac.c yarac_LDADD = -Llibyara/.libs -lyara yarac_DEPENDENCIES = libyara/.libs/libyara.la test_alignment_SOURCES = tests/test-alignment.c tests/util.c test_alignment_LDADD = libyara/.libs/libyara.a test_arena_SOURCES = tests/test-arena.c tests/util.c test_arena_LDADD = libyara/.libs/libyara.a test_atoms_SOURCES = tests/test-atoms.c tests/util.c test_atoms_LDADD = libyara/.libs/libyara.a test_rules_SOURCES = tests/test-rules.c tests/util.c test_rules_LDADD = libyara/.libs/libyara.a test_pe_SOURCES = tests/test-pe.c tests/util.c test_pe_LDADD = libyara/.libs/libyara.a test_elf_SOURCES = tests/test-elf.c tests/util.c test_elf_LDADD = libyara/.libs/libyara.a test_version_SOURCES = tests/test-version.c tests/util.c test_version_LDADD = libyara/.libs/libyara.a test_api_SOURCES = tests/test-api.c tests/util.c test_api_LDADD = libyara/.libs/libyara.a test_bitmask_SOURCES = tests/test-bitmask.c tests/util.c test_bitmask_LDADD = libyara/.libs/libyara.a test_math_SOURCES = tests/test-math.c tests/util.c test_math_LDADD = libyara/.libs/libyara.a test_stack_SOURCES = tests/test-stack.c tests/util.c test_stack_LDADD = libyara/.libs/libyara.a test_re_split_SOURCES = tests/test-re-split.c tests/util.c test_re_split_LDADD = libyara/.libs/libyara.a test_async_SOURCES = tests/test-async.c tests/util.c test_async_LDADD = libyara/.libs/libyara.a TESTS = $(check_PROGRAMS) TESTS_ENVIRONMENT = TOP_SRCDIR=$(top_srcdir) check_PROGRAMS = \ test-arena \ test-alignment \ test-atoms \ test-api \ test-rules \ test-pe \ test-elf \ test-version \ test-bitmask \ test-math \ test-stack \ test-re-split \ test-async if POSIX # The -fsanitize=address option makes test-exception fail. Include the test # only if the option is not enabled. if !ADDRESS_SANITIZER check_PROGRAMS+=test-exception test_exception_SOURCES = tests/test-exception.c tests/util.c test_exception_LDADD = libyara/.libs/libyara.a endif endif if MACHO_MODULE check_PROGRAMS+=test-macho test_macho_SOURCES = tests/test-macho.c tests/util.c test_macho_LDADD = libyara/.libs/libyara.a endif if DEX_MODULE check_PROGRAMS+=test-dex test_dex_SOURCES = tests/test-dex.c tests/util.c test_dex_LDADD = libyara/.libs/libyara.a endif if DOTNET_MODULE check_PROGRAMS+=test-dotnet test_dotnet_SOURCES = tests/test-dotnet.c tests/util.c test_dotnet_LDADD = libyara/.libs/libyara.a endif if PB_TESTS_MODULE check_PROGRAMS+=test-pb test_pb_SOURCES = tests/test-pb.c tests/util.c test_pb_LDADD = libyara/.libs/libyara.a endif # man pages man1_MANS = yara.man yarac.man EXTRA_DIST = $(man1_MANS) README.md bootstrap.sh yara-4.1.3/README.md000066400000000000000000000137701413423160300137370ustar00rootroot00000000000000[![Join the chat at https://gitter.im/VirusTotal/yara](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/VirusTotal/yara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Travis build status](https://travis-ci.com/VirusTotal/yara.svg)](https://travis-ci.com/VirusTotal/yara) [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/7glqg19w4oolm7pr?svg=true)](https://ci.appveyor.com/project/plusvic/yara) [![Coverity status](https://scan.coverity.com/projects/9057/badge.svg?flat=1)](https://scan.coverity.com/projects/plusvic-yara) ## YARA in a nutshell YARA is a tool aimed at (but not limited to) helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families (or whatever you want to describe) based on textual or binary patterns. Each description, a.k.a. rule, consists of a set of strings and a boolean expression which determine its logic. Let's see an example: ```yara rule silent_banker : banker { meta: description = "This is just an example" threat_level = 3 in_the_wild = true strings: $a = {6A 40 68 00 30 00 00 6A 14 8D 91} $b = {8D 4D B0 2B C1 83 C0 27 99 6A 4E 59 F7 F9} $c = "UVODFRYSIHLNWPEJXQZAKCBGMT" condition: $a or $b or $c } ``` The above rule is telling YARA that any file containing one of the three strings must be reported as *silent_banker*. This is just a simple example, more complex and powerful rules can be created by using wild-cards, case-insensitive strings, regular expressions, special operators and many other features that you'll find explained in [YARA's documentation](https://yara.readthedocs.org/). YARA is multi-platform, running on Windows, Linux and Mac OS X, and can be used through its command-line interface or from your own Python scripts with the yara-python extension. ## Additional resources Do you use GitHub for storing your YARA rules? [YARA-CI](https://yara-ci.cloud.virustotal.com) may be a useful addition to your toolbelt. This is GitHub application that provides continuous testing for your rules, helping you to identify common mistakes and false positives. If you plan to use YARA to scan compressed files (.zip, .tar, etc) you should take a look at [yextend](https://github.com/BayshoreNetworks/yextend), a very helpful extension to YARA developed and open-sourced by Bayshore Networks. Additionally, the guys from [InQuest](https://inquest.net/) have curated an awesome list of [YARA-related stuff](https://github.com/InQuest/awesome-yara). ## Who's using YARA * [ActiveCanopy](https://activecanopy.com/) * [Adlice](http://www.adlice.com/) * [AlienVault](https://otx.alienvault.com/) * [Avast](https://www.avast.com/) * [BAE Systems](http://www.baesystems.com/home?r=ai) * [Bayshore Networks, Inc.](http://www.bayshorenetworks.com) * [BinaryAlert](https://github.com/airbnb/binaryalert) * [Blue Coat](http://www.bluecoat.com/products/malware-analysis-appliance) * [Blueliv](http://www.blueliv.com) * [Claroty](https://claroty.com/continuous-threat-detection) * [Cofense](https://cofense.com) * [Conix](http://www.conix.fr) * [CounterCraft](https://www.countercraft.eu) * [CrowdStrike FMS](https://github.com/CrowdStrike/CrowdFMS) * [Cuckoo Sandbox](https://github.com/cuckoosandbox/cuckoo) * [Cyber Triage](http://www.cybertriage.com) * [Cybereason](https://www.cybereason.com) * [Digita Security](https://digitasecurity.com/product/uxprotect) * [Dragos Platform](https://dragos.com/platform/) * [Dtex Systems](https://dtexsystems.com) * [ESET](https://www.eset.com) * [ESTsecurity](https://www.estsecurity.com) * [Fidelis XPS](http://www.fidelissecurity.com/network-security-appliance/Fidelis-XPS) * [FireEye, Inc.](http://www.fireeye.com) * [Forcepoint](https://www.forcepoint.com) * [Fox-IT](https://www.fox-it.com) * [FSF](https://github.com/EmersonElectricCo/fsf) * [Guidance Software](http://www.guidancesoftware.com/endpointsecurity) * [Heroku](https://heroku.com) * [Hornetsecurity](https://www.hornetsecurity.com/en/) * [InQuest](http://www.inquest.net/) * [JASK](http://jask.io) * [Joe Security](https://www.joesecurity.org) * [jsunpack-n](http://jsunpack.jeek.org/) * [Kaspersky Lab](http://www.kaspersky.com) * [KnowBe4](https://www.knowbe4.com) * [Koodous](https://koodous.com/) * [Laika BOSS](https://github.com/lmco/laikaboss) * [Lastline, Inc.](http://www.lastline.com) * [LimaCharlie](https://limacharlie.io/) * [McAfee Advanced Threat Defense](http://mcafee.com/atd) * [Metaflows](http://www.metaflows.com) * [NBS System](https://www.nbs-system.com/) * [Nextron Systems](https://www.nextron-systems.com) * [Nozomi Networks](http://www.nozominetworks.com) * [osquery](http://www.osquery.io) * [Payload Security](https://www.payload-security.com) * [PhishMe](http://phishme.com/) * [Picus Security](http://www.picussecurity.com/) * [Radare2](http://rada.re) * [Raytheon Cyber Products, Inc.](http://www.raytheoncyber.com/capabilities/products/sureview-threatprotection/) * [RedSocks Security](https://redsocks.eu/) * [ReversingLabs](http://reversinglabs.com) * [root9B](https://www.root9b.com) * [RSA ECAT](http://www.emc.com/security/rsa-ecat.htm) * [Scanii](https://scanii.com) * [SecondWrite](https://www.secondwrite.com) * [SonicWall](https://www.sonicwall.com/) * [SpamStopsHere](https://www.spamstopshere.com) * [stoQ](http://stoq.punchcyber.com) * [Symantec](http://www.symantec.com) * [Tanium](http://www.tanium.com/) * [Tenable Network Security](https://www.tenable.com/) * [The DigiTrust Group](http://www.digitrustgroup.com/) * [ThreatConnect](https://www.threatconnect.com/) * [ThreatStream, Inc.](http://threatstream.com) * [Thug](https://github.com/buffer/thug) * [TouchWeb](https://www.touchweb.fr) * [Trend Micro](http://www.trendmicro.com) * [VirusTotal Intelligence](https://www.virustotal.com/intelligence/) * [VMRay](https://www.vmray.com/) * [We Watch Your Website](http://www.wewatchyourwebsite.com/) * [x64dbg](http://x64dbg.com) * [YALIH](https://github.com/Masood-M/YALIH) Are you using it? Want to see your site listed here? yara-4.1.3/WORKSPACE.bazel000066400000000000000000000036621413423160300150340ustar00rootroot00000000000000# Copyright (c) 2019. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Bazel (http://bazel.io/) WORKSPACE file for YARA. workspace(name = "com_github_virustotal_yara") load("//:bazel/yara_deps.bzl", "yara_deps") yara_deps() load( "@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains", ) rules_proto_dependencies() rules_proto_toolchains() load( "@com_google_sandboxed_api//sandboxed_api/bazel:sapi_deps.bzl", "sapi_deps", ) sapi_deps() yara-4.1.3/appveyor.yml000066400000000000000000000057001413423160300150420ustar00rootroot00000000000000# AppVeyor CI for Windows version: '{branch}-{build}' pull_requests: do_not_increment_build_number: true environment: matrix: - TARGET: vs2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 VisualStudioVersion: 14.0 platform: x86 configuration: Release artifact_postfix: win32 - TARGET: vs2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 VisualStudioVersion: 14.0 platform: x86 configuration: Debug - TARGET: vs2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 VisualStudioVersion: 14.0 platform: x64 configuration: Release artifact_postfix: win64 - TARGET: vs2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 VisualStudioVersion: 14.0 platform: x64 configuration: Debug - TARGET: cygwin for: - matrix: only: - TARGET: cygwin # Disable the installation of flex and bison packages. For some reason this # now (2021-04-19) upgrades the perl_base package to version 5.32.1-1 which # later fails with error: # Can't locate threads.pm in @INC (you may need to install the threads module) # # before_build: # - cmd: C:\cygwin64\setup-x86_64.exe --quiet-mode --no-desktop --no-shortcuts --no-startmenu --no-admin --packages flex,bison build_script: - cmd: C:\cygwin64\bin\bash -e -l -c "cd c:/projects/yara && ./build.sh" test_script: - cmd: C:\cygwin64\bin\bash -e -l -c "cd c:/projects/yara && make check" - matrix: only: - TARGET: vs2015 - configuration: Release before_build: - ps: nuget restore windows/vs2015/yara.sln build: project: windows/vs2015/yara.sln verbosity: minimal after_build: - cmd: 7z a yara-%APPVEYOR_BUILD_VERSION%-%ARTIFACT_POSTFIX%.zip %APPVEYOR_BUILD_FOLDER%\windows\%TARGET%\%CONFIGURATION%\yara*.exe artifacts: - path: yara-$(APPVEYOR_BUILD_VERSION)-$(ARTIFACT_POSTFIX).zip name: yara-$(APPVEYOR_BUILD_VERSION)-$(ARTIFACT_POSTFIX) type: zip deploy: tag: $(APPVEYOR_REPO_TAG_NAME) release: YARA $(APPVEYOR_REPO_TAG_NAME) provider: GitHub auth_token: secure: d3qqX7bmrBiKJI38yFPc5vHrGGfS3LxLC7FaG6ewI2ghPPE22Pk6QtyrEFFb73PL artifact: yara-$(APPVEYOR_BUILD_VERSION)-$(ARTIFACT_POSTFIX) draft: true on: APPVEYOR_REPO_TAG: true # deploy on tag push only test_script: - cmd: c:/projects/yara/windows/vs2015/%CONFIGURATION%/test-alignment.exe - matrix: only: - TARGET: vs2015 - configuration: Debug before_build: - ps: nuget restore windows/vs2015/yara.sln build: project: windows/vs2015/yara.sln verbosity: minimal test_script: - cmd: c:/projects/yara/windows/vs2015/%CONFIGURATION%/test-alignment.exe # Uncomment the lines below for enabling Remote Desktop in the Appveyor. This # allows connecting to the remote machine and debug issues. # on_finish: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) yara-4.1.3/bazel/000077500000000000000000000000001413423160300135455ustar00rootroot00000000000000yara-4.1.3/bazel/jansson.BUILD000066400000000000000000000061761413423160300160130ustar00rootroot00000000000000# Copyright (c) 2020. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Bazel (http://bazel.io/) BUILD file for the jansson library. # Rule for generating header files required during the build. These header files # are normally generated by ./configure, but we just create empty files so that # #include statements can find them. genrule( name = "config_files", outs = [ "jansson_config.h", "jansson_private_config.h", ], cmd = """ touch $(location jansson_config.h) touch $(location jansson_private_config.h) """, ) cc_library( name = "jansson", srcs = glob(["src/*.c"]), hdrs = glob(["src/*.h"]) + [ "jansson_config.h", "jansson_private_config.h", ], copts = ["-Wno-unused-function"], # These defines are usually in jansson_config.h but we define them here, # as jansson_config.h is just an empty file. defines = [ "HAVE_STDINT_H=1", "JSON_INLINE=inline", "JSON_PARSER_MAX_DEPTH=2048", ], includes = [ ".", "src", ], visibility = ["//visibility:public"], ) load("@com_github_virustotal_yara//:bazel/jansson.bzl", "jansson_api_test") jansson_api_test("test_array") jansson_api_test("test_chaos") jansson_api_test("test_copy") jansson_api_test("test_dump") jansson_api_test("test_dump_callback") jansson_api_test("test_equal") jansson_api_test("test_load") jansson_api_test("test_load_callback") jansson_api_test("test_loadb") jansson_api_test("test_memory_funcs") jansson_api_test("test_number") jansson_api_test("test_object") jansson_api_test("test_pack") jansson_api_test("test_simple") jansson_api_test("test_sprintf") jansson_api_test("test_unpack") yara-4.1.3/bazel/jansson.bzl000066400000000000000000000033301413423160300157300ustar00rootroot00000000000000# Copyright (c) 2020. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. def jansson_api_test(name): native.cc_test( name = name, srcs = [ "test/suites/api/util.h", "test/suites/api/" + name + ".c", ], deps = [":jansson"], ) yara-4.1.3/bazel/magic.BUILD000066400000000000000000000112701413423160300154070ustar00rootroot00000000000000# Copyright (c) 2020. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Bazel (http://bazel.io/) BUILD file for the libmagic library. # Rule for creating magic.h from magic.h.in. The two files are identical, except # for magic.h.in not having the actual version number. Instead it has a X.YY # placeholder that is replaced with the version number (i.e: 538) during build # time. When this library is updated the version number in this rule must be # updated accordingly. genrule( name = "magic_h", srcs = ["src/magic.h.in"], outs = ["src/magic.h"], cmd = """ sed -e 's/X.YY/538/' < $(location src/magic.h.in) > $(location src/magic.h) """, ) MAGIC_COPTS = [ # The VERSION macro usually contains the actual version (e.g: "5.38") but # any arbitrary string will work. We simply use "BUILT_BY_YARA". "-DVERSION=\\\"BUILT_BY_YARA\\\"", ] + select({ "@bazel_tools//src/conditions:darwin": [ "-DHAVE_FORK=1", "-DHAVE_INTTYPES_H=1", "-DHAVE_STDINT_H=1", "-DHAVE_STRLCAT=1", "-DHAVE_STRLCPY=1", "-DHAVE_UNISTD_H=1", ], "@bazel_tools//src/conditions:freebsd": [ "-DHAVE_FORK=1", "-DHAVE_INTTYPES_H=1", "-DHAVE_STDINT_H=1", "-DHAVE_STRLCAT=1", "-DHAVE_STRLCPY=1", "-DHAVE_UNISTD_H=1", ], "@bazel_tools//src/conditions:linux_aarch64": [ "-DHAVE_FORK=1", "-DHAVE_INTTYPES_H=1", "-DHAVE_STDINT_H=1", "-DHAVE_UNISTD_H=1", ], "@bazel_tools//src/conditions:linux_x86_64": [ "-DHAVE_FORK=1", "-DHAVE_INTTYPES_H=1", "-DHAVE_STDINT_H=1", "-DHAVE_UNISTD_H=1", "-DHAVE_MKSTEMP=1", ], "@bazel_tools//src/conditions:windows": [ ], }) cc_library( name = "magic", srcs = glob(["src/*.h"]) + [ "src/apprentice.c", "src/apptype.c", "src/ascmagic.c", "src/asctime_r.c", "src/asprintf.c", "src/buffer.c", "src/cdf.c", "src/cdf_time.c", "src/compress.c", "src/ctime_r.c", "src/der.c", "src/dprintf.c", "src/encoding.c", "src/fmtcheck.c", "src/fsmagic.c", "src/funcs.c", "src/getline.c", "src/getopt_long.c", "src/gmtime_r.c", "src/is_csv.c", "src/is_json.c", "src/is_tar.c", "src/localtime_r.c", "src/magic.c", "src/pread.c", "src/print.c", "src/readcdf.c", "src/readelf.c", "src/seccomp.c", "src/softmagic.c", "src/strcasestr.c", "src/teststrchr.c", "src/vasprintf.c", ] + select({ "@bazel_tools//src/conditions:darwin": [ ], "@bazel_tools//src/conditions:freebsd": [ ], "@bazel_tools//src/conditions:linux_aarch64": [ "src/strlcat.c", "src/strlcpy.c", ], "@bazel_tools//src/conditions:linux_x86_64": [ "src/strlcat.c", "src/strlcpy.c", ], "@bazel_tools//src/conditions:windows": [ "src/strlcat.c", "src/strlcpy.c", ], }), hdrs = ["src/magic.h"], copts = MAGIC_COPTS, includes = [ ".", "src", ], visibility = ["//visibility:public"], ) yara-4.1.3/bazel/openssl.BUILD000066400000000000000000000060701413423160300160140ustar00rootroot00000000000000# Copyright (c) 2020. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Bazel (http://bazel.io/) BUILD file for openssl library which is a dependency # for YARA. config_setting( name = "darwin", values = { "cpu": "darwin_x86_64", }, ) cc_library( name = "crypto", srcs = ["libcrypto.a"], hdrs = glob(["include/openssl/*.h"]) + ["include/openssl/opensslconf.h"], includes = ["include"], linkopts = select({ ":darwin": [], "//conditions:default": [ "-lpthread", "-ldl", ], }), visibility = ["//visibility:public"], ) cc_library( name = "ssl", srcs = ["libssl.a"], hdrs = glob(["include/openssl/*.h"]) + ["include/openssl/opensslconf.h"], includes = ["include"], visibility = ["//visibility:public"], deps = [":crypto"], ) genrule( name = "openssl-build", srcs = glob( ["**/*"], exclude = ["bazel-*"], ), outs = [ "libcrypto.a", "libssl.a", "include/openssl/opensslconf.h", ], cmd = """ CONFIG_LOG=$$(mktemp) MAKE_LOG=$$(mktemp) OPENSSL_ROOT=$$(dirname $(location config)) pushd $$OPENSSL_ROOT > /dev/null if ! ./config > $$CONFIG_LOG; then cat $$CONFIG_LOG fi if ! make -j 4 > $$MAKE_LOG; then cat $$MAKE_LOG fi popd > /dev/null cp $$OPENSSL_ROOT/libcrypto.a $(location libcrypto.a) cp $$OPENSSL_ROOT/libssl.a $(location libssl.a) cp $$OPENSSL_ROOT/include/openssl/opensslconf.h $(location include/openssl/opensslconf.h) """, ) yara-4.1.3/bazel/yara.bzl000066400000000000000000000153121413423160300152140ustar00rootroot00000000000000YARA_CONFIG_OPTS = [ "-DHAVE_CLOCK_GETTIME=1", #"-DHAVE_COMMONCRYPTO_COMMONCRYPTO_H", "-DHAVE_LIBCRYPTO=1", "-DHAVE_MEMMEM=1", "-DHAVE_STDBOOL_H=1", # "-DHAVE__MKGMTIME=1", "-DHAVE_TIMEGM=1", ] YARA_COPTS = YARA_CONFIG_OPTS + [ "-D_GNU_SOURCE", "-O3", "-DNDEBUG", # Tell the compiler we want the C99 standard. "-std=c99", # Additional include paths. "-Ilibyara", "-Ilibyara/modules", ] + select({ "@bazel_tools//src/conditions:darwin": [ "-DUSE_MACH_PROC", "-DHAVE_SCAN_PROC_IMPL=1", "-DHAVE_STRLCAT=1", "-DHAVE_STRLCPY=1", ], "@bazel_tools//src/conditions:freebsd": [ "-DUSE_FREEBSD_PROC", "-DHAVE_SCAN_PROC_IMPL=1", "-DHAVE_STRLCAT=1", "-DHAVE_STRLCPY=1", ], "@bazel_tools//src/conditions:linux_aarch64": [ "-DUSE_LINUX_PROC", "-DHAVE_SCAN_PROC_IMPL=1", ], "@bazel_tools//src/conditions:linux_x86_64": [ "-DUSE_LINUX_PROC", "-DHAVE_SCAN_PROC_IMPL=1", ], "@bazel_tools//src/conditions:windows": [ "-DUSE_WINDOWS_PROC", "-DHAVE_SCAN_PROC_IMPL=1", ], "//conditions:default": ["-DUSE_NO_PROC"], }) # Define rule for generating the module_list file. This rule has an attribute # "modules" which is a list of module names that we want in the file. def _module_list_impl(ctx): output = ctx.outputs.out content = "\n".join(["MODULE(%s)" % m for m in ctx.attr.modules]) ctx.actions.write(output = output, content = content) module_list = rule( implementation = _module_list_impl, attrs = {"modules": attr.string_list()}, outputs = {"out": "libyara/modules/module_list"}, ) def yara_library( name, defines = [], modules = [], modules_srcs = [], deps = [], copts = YARA_COPTS, crypto_libs = ["@openssl//:crypto"]): """Macro for generating the YARA library with a specific list of modules. This macro allows to cherry-pick the modules that you want to build into the library. For example, for building it with modules pe and elf only, you can use: yara_library( name = "libyara", modules = [ "cuckoo", "dex", "dotnet", "elf", ], modules_srcs = [ "libyara/modules/elf/elf.c", "libyara/modules/pe/pe.c", "libyara/modules/pe/pe_utils.c", ] ) The "modules_srcs" list must contain the source files implementing the modules listed in the "modules" argument. """ module_list( name = "module_list", modules = modules, ) native.cc_library( name = name, defines = defines + [m.upper() + "_MODULE" for m in modules], srcs = modules_srcs + [ "libyara/ahocorasick.c", "libyara/arena.c", "libyara/atoms.c", "libyara/base64.c", "libyara/bitmask.c", "libyara/compiler.c", "libyara/crypto.h", "libyara/endian.c", "libyara/exception.h", "libyara/exec.c", "libyara/exefiles.c", "libyara/filemap.c", "libyara/grammar.c", "libyara/hash.c", "libyara/hex_grammar.c", "libyara/hex_grammar.h", "libyara/hex_lexer.c", "libyara/include/yara.h", "libyara/include/yara/ahocorasick.h", "libyara/include/yara/arena.h", "libyara/include/yara/atoms.h", "libyara/include/yara/base64.h", "libyara/include/yara/bitmask.h", "libyara/include/yara/compiler.h", "libyara/include/yara/dex.h", "libyara/include/yara/dotnet.h", "libyara/include/yara/elf.h", "libyara/include/yara/endian.h", "libyara/include/yara/error.h", "libyara/include/yara/exec.h", "libyara/include/yara/exefiles.h", "libyara/include/yara/filemap.h", "libyara/include/yara/globals.h", "libyara/include/yara/hash.h", "libyara/include/yara/hex_lexer.h", "libyara/include/yara/integers.h", "libyara/include/yara/lexer.h", "libyara/include/yara/libyara.h", "libyara/include/yara/limits.h", "libyara/include/yara/macho.h", "libyara/include/yara/mem.h", "libyara/include/yara/modules.h", "libyara/include/yara/notebook.h", "libyara/include/yara/object.h", "libyara/include/yara/parser.h", "libyara/include/yara/pe.h", "libyara/include/yara/pe_utils.h", "libyara/include/yara/proc.h", "libyara/include/yara/re.h", "libyara/include/yara/re_lexer.h", "libyara/include/yara/rules.h", "libyara/include/yara/scan.h", "libyara/include/yara/scanner.h", "libyara/include/yara/sizedstr.h", "libyara/include/yara/stack.h", "libyara/include/yara/stopwatch.h", "libyara/include/yara/stream.h", "libyara/include/yara/strutils.h", "libyara/include/yara/threading.h", "libyara/include/yara/types.h", "libyara/include/yara/utils.h", "libyara/lexer.c", "libyara/libyara.c", "libyara/mem.c", "libyara/modules.c", "libyara/notebook.c", "libyara/object.c", "libyara/parser.c", "libyara/proc.c", "libyara/proc/freebsd.c", "libyara/proc/linux.c", "libyara/proc/mach.c", "libyara/proc/none.c", "libyara/proc/openbsd.c", "libyara/proc/windows.c", "libyara/re.c", "libyara/re_grammar.c", "libyara/re_grammar.h", "libyara/re_lexer.c", "libyara/rules.c", "libyara/scan.c", "libyara/scanner.c", "libyara/sizedstr.c", "libyara/stack.c", "libyara/stopwatch.c", "libyara/stream.c", "libyara/strutils.c", "libyara/threading.c", ], hdrs = [ "libyara/include/yara.h", "libyara/include/yara/pe.h", "libyara/include/yara/proc.h", "libyara/include/yara/rules.h", ], copts = copts, includes = [ "libyara/modules", "libyara/include", "libyara", ], textual_hdrs = [ ":module_list", "libyara/grammar.h", "libyara/hex_grammar.y", "libyara/re_grammar.y", ], deps = deps + crypto_libs, visibility = ["//visibility:public"], ) yara-4.1.3/bazel/yara_deps.bzl000066400000000000000000000106231413423160300162270ustar00rootroot00000000000000# Copyright (c) 2020. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Load dependencies needed to compile YARA as a 3rd-party consumer.""" load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") def yara_deps(): """Loads common dependencies needed to compile YARA.""" maybe( http_archive, name = "openssl", url = "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0h.tar.gz", sha256 = "f56dd7d81ce8d3e395f83285bd700a1098ed5a4cb0a81ce9522e41e6db7e0389", strip_prefix = "openssl-OpenSSL_1_1_0h", build_file = "@com_github_virustotal_yara//:bazel/openssl.BUILD", ) maybe( git_repository, name = "boringssl", commit = "095d78b14f91cc9a910408eaae84a3bdafc54da9", # 2019-06-05 remote = "https://boringssl.googlesource.com/boringssl", shallow_since = "1559759280 +0000", ) maybe( http_archive, name = "jansson", url = "https://github.com/akheron/jansson/archive/v2.12.tar.gz", sha256 = "76260d30e9bbd0ef392798525e8cd7fe59a6450c54ca6135672e3cd6a1642941", strip_prefix = "jansson-2.12", build_file = "@com_github_virustotal_yara//:bazel/jansson.BUILD", ) maybe( # When updating this dependency to a more recent version, the version # in the bazel/magic.BUILD must be updated acordingly. http_archive, name = "magic", url = "https://github.com/file/file/archive/FILE5_38.tar.gz", sha256 = "338ebe8cb536a3f86750b4df62be2d382f6da66afdb0087b36a1a3e14ea4baf8", strip_prefix = "file-FILE5_38", build_file = "@com_github_virustotal_yara//:bazel/magic.BUILD", ) maybe( git_repository, name = "com_google_sandboxed_api", commit = "144a441d798f13d27cc71e7c2a630e0063d747b5", # 2020-05-12 remote = "https://github.com/google/sandboxed-api.git", shallow_since = "1589270865 -0700", ) maybe( http_archive, name = "rules_proto", sha256 = "602e7161d9195e50246177e7c55b2f39950a9cf7366f74ed5f22fd45750cd208", strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313", urls = [ "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", "https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz", ], ) maybe( # GoogleTest/GoogleMock for testing the sandbox http_archive, name = "com_google_googletest", sha256 = "ba5b04a4849246e7c16ba94227eed46486ef942f61dc8b78609732543c19c9f4", # 2019-11-21 strip_prefix = "googletest-200ff599496e20f4e39566feeaf2f6734ca7570f", urls = ["https://github.com/google/googletest/archive/200ff599496e20f4e39566feeaf2f6734ca7570f.zip"], ) yara-4.1.3/bootstrap.sh000077500000000000000000000000471413423160300150250ustar00rootroot00000000000000#!/bin/sh autoreconf --force --install yara-4.1.3/build.sh000077500000000000000000000000511413423160300141020ustar00rootroot00000000000000#!/bin/sh ./bootstrap.sh ./configure makeyara-4.1.3/cli/000077500000000000000000000000001413423160300132175ustar00rootroot00000000000000yara-4.1.3/cli/args.c000066400000000000000000000152201413423160300143170ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "args.h" #include #include #include #include #define args_is_long_arg(arg) (arg[0] == '-' && arg[1] == '-' && arg[2] != '\0') #define args_is_short_arg(arg) \ (arg[0] == '-' && arg[1] != '-' && arg[1] != '\0') args_option_t* args_get_short_option(args_option_t* options, const char opt) { while (options->type != ARGS_OPT_END) { if (opt == options->short_name) return options; options++; } return NULL; } args_option_t* args_get_long_option(args_option_t* options, const char* arg) { arg += 2; // skip starting -- while (options->type != ARGS_OPT_END) { if (options->long_name != NULL) { size_t l = strlen(options->long_name); if ((arg[l] == '\0' || arg[l] == '=') && strstr(arg, options->long_name) == arg) { return options; } } options++; } return NULL; } args_error_type_t args_parse_option( args_option_t* opt, const char* opt_arg, int* opt_arg_was_used) { char* endptr = NULL; if (opt_arg_was_used != NULL) *opt_arg_was_used = 0; if (opt->count == opt->max_count) return ARGS_ERROR_TOO_MANY; switch (opt->type) { case ARGS_OPT_BOOLEAN: *(bool*) opt->value = !(*(bool*) opt->value); break; case ARGS_OPT_INTEGER: if (opt_arg == NULL) return ARGS_ERROR_REQUIRED_INTEGER_ARG; *(int*) opt->value = strtol(opt_arg, &endptr, 0); if (*endptr != '\0') return ARGS_ERROR_REQUIRED_INTEGER_ARG; if (opt_arg_was_used != NULL) *opt_arg_was_used = 1; break; case ARGS_OPT_STRING: if (opt_arg == NULL) return ARGS_ERROR_REQUIRED_STRING_ARG; if (opt->max_count > 1) ((const char**) opt->value)[opt->count] = opt_arg; else *(const char**) opt->value = opt_arg; if (opt_arg_was_used != NULL) *opt_arg_was_used = 1; break; default: assert(0); } opt->count++; return ARGS_ERROR_OK; } void args_print_error(args_error_type_t error, const char* option) { switch (error) { case ARGS_ERROR_UNKNOWN_OPT: fprintf(stderr, "unknown option `%s`\n", option); break; case ARGS_ERROR_TOO_MANY: fprintf(stderr, "too many `%s` options\n", option); break; case ARGS_ERROR_REQUIRED_INTEGER_ARG: fprintf(stderr, "option `%s` requires an integer argument\n", option); break; case ARGS_ERROR_REQUIRED_STRING_ARG: fprintf(stderr, "option `%s` requires a string argument\n", option); break; case ARGS_ERROR_UNEXPECTED_ARG: fprintf(stderr, "option `%s` doesn't expect an argument\n", option); break; default: return; } } int args_parse(args_option_t* options, int argc, const char** argv) { args_error_type_t error = ARGS_ERROR_OK; int i = 1; // start with i = 1, argv[0] is the program name int o = 0; while (i < argc) { const char* arg = argv[i]; if (args_is_long_arg(arg)) { args_option_t* opt = args_get_long_option(options, arg); if (opt != NULL) { const char* equal = strchr(arg, '='); if (equal) error = args_parse_option(opt, equal + 1, NULL); else error = args_parse_option(opt, NULL, NULL); } else { error = ARGS_ERROR_UNKNOWN_OPT; } } else if (args_is_short_arg(arg)) { for (int j = 1; arg[j] != '\0'; j++) { args_option_t* opt = args_get_short_option(options, arg[j]); if (opt != NULL) { if (arg[j + 1] == '\0') { int arg_used; // short option followed by a space, argv[i + 1] could be // an argument for the option (i.e: -a ) error = args_parse_option(opt, argv[i + 1], &arg_used); // argv[i + 1] was actually an argument to the option, skip it. if (arg_used) i++; } else { // short option followed by another option (i.e: -ab), no // argument for this option error = args_parse_option(opt, NULL, NULL); } } else { error = ARGS_ERROR_UNKNOWN_OPT; } if (error != ARGS_ERROR_OK) break; } } else { argv[o++] = arg; } if (error != ARGS_ERROR_OK) { args_print_error(error, arg); exit(1); } i++; } return o; } void args_print_usage(args_option_t* options, int help_alignment) { char buffer[128]; for (; options->type != ARGS_OPT_END; options++) { int len = sprintf(buffer, " "); if (options->short_name != '\0') len += sprintf(buffer + len, "-%c", options->short_name); else len += sprintf(buffer + len, " "); if (options->short_name != '\0' && options->long_name != NULL) len += sprintf(buffer + len, ", "); if (options->long_name != NULL) len += sprintf(buffer + len, "--%s", options->long_name); if (options->type == ARGS_OPT_STRING || options->type == ARGS_OPT_INTEGER) { len += sprintf( buffer + len, "%s%s", (options->long_name != NULL) ? "=" : " ", options->type_help); } printf("%-*s%s\n", help_alignment, buffer, options->help); } } yara-4.1.3/cli/args.h000066400000000000000000000064441413423160300143340ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef ARGPARSE_H #define ARGPARSE_H #include #ifdef __cplusplus extern "C" { #endif typedef enum _args_error_type { ARGS_ERROR_OK, ARGS_ERROR_UNKNOWN_OPT, ARGS_ERROR_TOO_MANY, ARGS_ERROR_REQUIRED_INTEGER_ARG, ARGS_ERROR_REQUIRED_STRING_ARG, ARGS_ERROR_UNEXPECTED_ARG, } args_error_type_t; typedef enum _args_option_type { // special ARGS_OPT_END, ARGS_OPT_GROUP, // options with no arguments ARGS_OPT_BOOLEAN, // options with arguments (optional or required) ARGS_OPT_INTEGER, ARGS_OPT_STRING, } args_option_type_t; typedef struct _args_option { args_option_type_t type; const char short_name; const char *long_name; void *value; int max_count; const char *help; const char *type_help; int count; } args_option_t; #define OPT_BOOLEAN(short_name, long_name, value, ...) \ { \ ARGS_OPT_BOOLEAN, short_name, long_name, value, 1, __VA_ARGS__ \ } #define OPT_INTEGER(short_name, long_name, value, ...) \ { \ ARGS_OPT_INTEGER, short_name, long_name, value, 1, __VA_ARGS__ \ } #define OPT_STRING_MULTI(short_name, long_name, value, max_count, ...) \ { \ ARGS_OPT_STRING, short_name, long_name, value, max_count, __VA_ARGS__ \ } #define OPT_STRING(short_name, long_name, value, ...) \ OPT_STRING_MULTI(short_name, long_name, value, 1, __VA_ARGS__) #define OPT_END() \ { \ ARGS_OPT_END, 0 \ } int args_parse(args_option_t *options, int argc, const char **argv); void args_print_usage(args_option_t *options, int alignment); #ifdef __cplusplus } #endif #endif yara-4.1.3/cli/common.h000066400000000000000000000072151413423160300146650ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef COMMON_H #define COMMON_H #include #ifdef _WIN32 #include #define access _access_s #else #include #endif #define exit_with_code(code) \ { \ result = code; \ goto _exit; \ } bool compile_files(YR_COMPILER* compiler, int argc, const char** argv) { for (int i = 0; i < argc - 1; i++) { FILE* rule_file; const char* ns; const char* file_name; char* colon = NULL; int errors; if (access(argv[i], 0) != 0) { // A file with the name specified by the command-line argument wasn't // found, it may be because the name is prefixed with a namespace, so // lets try to find the colon that separates the namespace from the /// actual file name. colon = (char*) strchr(argv[i], ':'); } // The namespace delimiter must be a colon not followed by a slash or // backslash. if (colon && *(colon + 1) != '\\' && *(colon + 1) != '/') { file_name = colon + 1; *colon = '\0'; ns = argv[i]; } else { file_name = argv[i]; ns = NULL; } if (strcmp(file_name, "-") == 0) rule_file = stdin; else rule_file = fopen(file_name, "r"); if (rule_file == NULL) { fprintf(stderr, "error: could not open file: %s\n", file_name); return false; } errors = yr_compiler_add_file(compiler, rule_file, ns, file_name); fclose(rule_file); if (errors > 0) return false; } return true; } bool is_integer(const char* str) { if (*str == '-') str++; if (*str == '\0') return false; while (*str) { if (!isdigit(*str)) return false; str++; } return true; } bool is_float(const char* str) { bool has_dot = false; if (*str == '-') // skip the minus sign if present str++; if (*str == '.') // float can't start with a dot return false; while (*str) { if (*str == '.') { if (has_dot) // two dots, not a float return false; has_dot = true; } else if (!isdigit(*str)) { return false; } str++; } return has_dot; // to be float must contain a dot } #endif yara-4.1.3/cli/threading.c000066400000000000000000000121271413423160300153330ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if !defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #endif #if defined(__APPLE__) #include #include #else #include #endif #include #include #include "threading.h" int cli_mutex_init(MUTEX* mutex) { #if defined(_WIN32) || defined(__CYGWIN__) InitializeCriticalSection(mutex); return 0; #else return pthread_mutex_init(mutex, NULL); #endif } void cli_mutex_destroy(MUTEX* mutex) { #if defined(_WIN32) || defined(__CYGWIN__) DeleteCriticalSection(mutex); #else pthread_mutex_destroy(mutex); #endif } void cli_mutex_lock(MUTEX* mutex) { #if defined(_WIN32) || defined(__CYGWIN__) EnterCriticalSection(mutex); #else pthread_mutex_lock(mutex); #endif } void cli_mutex_unlock(MUTEX* mutex) { #if defined(_WIN32) || defined(__CYGWIN__) LeaveCriticalSection(mutex); #else pthread_mutex_unlock(mutex); #endif } int cli_semaphore_init(SEMAPHORE* semaphore, int value) { #if defined(_WIN32) || defined(__CYGWIN__) *semaphore = CreateSemaphore(NULL, value, 65535, NULL); if (*semaphore == NULL) return GetLastError(); #elif defined(__APPLE__) int result = semaphore_create( mach_task_self(), semaphore, SYNC_POLICY_FIFO, value); if (result != KERN_SUCCESS) return result; #else *semaphore = malloc(sizeof(sem_t)); if (*semaphore == NULL) return errno; return sem_init(*semaphore, 0, value); #endif return 0; } void cli_semaphore_destroy(SEMAPHORE* semaphore) { #if defined(_WIN32) || defined(__CYGWIN__) CloseHandle(*semaphore); #elif defined(__APPLE__) semaphore_destroy(mach_task_self(), *semaphore); #else sem_close(*semaphore); free(*semaphore); #endif } /////////////////////////////////////////////////////////////////////////////// // Wait for the semaphore, but stop waiting when the current time exceeds the // given deadline. If deadline is in the past, exit immediately. // int cli_semaphore_wait(SEMAPHORE* semaphore, time_t deadline) { unsigned int timeout = (unsigned int) (deadline - time(NULL)); if (timeout <= 0) return ERROR_SCAN_TIMEOUT; #if defined(_WIN32) || defined(__CYGWIN__) if (WaitForSingleObject(*semaphore, timeout * 1000) == WAIT_TIMEOUT) return ERROR_SCAN_TIMEOUT; #elif defined(__APPLE__) mach_timespec_t ts; // semaphore_timedwait expects a timeout relative to the current time, not an // absolute timeout (deadline) as sem_timedwait does. ts.tv_sec = timeout; ts.tv_nsec = 0; if (semaphore_timedwait(*semaphore, ts) == KERN_OPERATION_TIMED_OUT) return ERROR_SCAN_TIMEOUT; #else struct timespec ts; // sem_timedwait expects an absolute timeout (deadline), not a relative // timeout as semaphore_timedwait does. ts.tv_sec = deadline; ts.tv_nsec = 0; if (sem_timedwait(*semaphore, &ts) == -1 && errno == ETIMEDOUT) return ERROR_SCAN_TIMEOUT; #endif return ERROR_SUCCESS; } void cli_semaphore_release(SEMAPHORE* semaphore) { #if defined(_WIN32) || defined(__CYGWIN__) ReleaseSemaphore(*semaphore, 1, NULL); #elif defined(__APPLE__) semaphore_signal(*semaphore); #else sem_post(*semaphore); #endif } int cli_create_thread( THREAD* thread, THREAD_START_ROUTINE start_routine, void* param) { #if defined(_WIN32) || defined(__CYGWIN__) *thread = CreateThread(NULL, 0, start_routine, param, 0, NULL); if (*thread == NULL) return GetLastError(); else return 0; #else return pthread_create(thread, NULL, start_routine, param); #endif } void cli_thread_join(THREAD* thread) { #if defined(_WIN32) || defined(__CYGWIN__) WaitForSingleObject(*thread, INFINITE); #else pthread_join(*thread, NULL); #endif } yara-4.1.3/cli/threading.h000066400000000000000000000051441413423160300153410ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef THREADING_H #define THREADING_H #if defined(_WIN32) || defined(__CYGWIN__) #include #else #include #include #if defined(__APPLE__) #include #else #include #endif #endif #if defined(_WIN32) || defined(__CYGWIN__) typedef HANDLE SEMAPHORE; typedef CRITICAL_SECTION MUTEX; typedef HANDLE THREAD; typedef LPTHREAD_START_ROUTINE THREAD_START_ROUTINE; #else typedef pthread_mutex_t MUTEX; typedef pthread_t THREAD; typedef void* (*THREAD_START_ROUTINE)(void*); #if defined(__APPLE__) typedef semaphore_t SEMAPHORE; #else typedef sem_t* SEMAPHORE; #endif #endif int cli_mutex_init(MUTEX* mutex); void cli_mutex_destroy(MUTEX* mutex); void cli_mutex_lock(MUTEX* mutex); void cli_mutex_unlock(MUTEX* mutex); int cli_semaphore_init(SEMAPHORE* semaphore, int value); void cli_semaphore_destroy(SEMAPHORE* semaphore); int cli_semaphore_wait(SEMAPHORE* semaphore, time_t abs_timeout); void cli_semaphore_release(SEMAPHORE* semaphore); int cli_create_thread( THREAD* thread, THREAD_START_ROUTINE start_routine, void* param); void cli_thread_join(THREAD* thread); #endif yara-4.1.3/cli/yara.c000066400000000000000000001062241413423160300143240ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if !defined(_WIN32) && !defined(__CYGWIN__) // for getline(3) #define _POSIX_C_SOURCE 200809L #include #include #include #include #else #include #define PRIx64 "I64x" #define PRId64 "I64d" #endif #include #include #include #include #include #include #include "args.h" #include "common.h" #include "threading.h" #define ERROR_COULD_NOT_CREATE_THREAD 100 #ifndef MAX_PATH #define MAX_PATH 256 #endif #ifndef min #define min(x, y) ((x < y) ? (x) : (y)) #endif #ifdef _MSC_VER #define snprintf _snprintf #define strdup _strdup #endif #define MAX_QUEUED_FILES 64 typedef struct _MODULE_DATA { const char* module_name; YR_MAPPED_FILE mapped_file; struct _MODULE_DATA* next; } MODULE_DATA; typedef struct _CALLBACK_ARGS { const char* file_path; int current_count; } CALLBACK_ARGS; typedef struct _THREAD_ARGS { YR_SCANNER* scanner; CALLBACK_ARGS callback_args; time_t deadline; int current_count; } THREAD_ARGS; typedef struct _QUEUED_FILE { char* path; } QUEUED_FILE; typedef struct COMPILER_RESULTS { int errors; int warnings; } COMPILER_RESULTS; typedef struct SCAN_OPTIONS { bool follow_symlinks; bool recursive_search; time_t deadline; } SCAN_OPTIONS; #define MAX_ARGS_TAG 32 #define MAX_ARGS_IDENTIFIER 32 #define MAX_ARGS_EXT_VAR 32 #define MAX_ARGS_MODULE_DATA 32 static char* atom_quality_table; static char* tags[MAX_ARGS_TAG + 1]; static char* identifiers[MAX_ARGS_IDENTIFIER + 1]; static char* ext_vars[MAX_ARGS_EXT_VAR + 1]; static char* modules_data[MAX_ARGS_MODULE_DATA + 1]; static bool follow_symlinks = true; static bool recursive_search = false; static bool scan_list_search = false; static bool show_module_data = false; static bool show_tags = false; static bool show_stats = false; static bool show_strings = false; static bool show_string_length = false; static bool show_meta = false; static bool show_namespace = false; static bool show_version = false; static bool show_help = false; static bool ignore_warnings = false; static bool fast_scan = false; static bool negate = false; static bool print_count_only = false; static bool fail_on_warnings = false; static bool rules_are_compiled = false; static int total_count = 0; static int limit = 0; static int timeout = 1000000; static int stack_size = DEFAULT_STACK_SIZE; static int threads = YR_MAX_THREADS; static int max_strings_per_rule = DEFAULT_MAX_STRINGS_PER_RULE; #define USAGE_STRING \ "Usage: yara [OPTION]... [NAMESPACE:]RULES_FILE... FILE | DIR | PID" args_option_t options[] = { OPT_STRING( 0, "atom-quality-table", &atom_quality_table, "path to a file with the atom quality table", "FILE"), OPT_BOOLEAN( 'C', "compiled-rules", &rules_are_compiled, "load compiled rules"), OPT_BOOLEAN( 'c', "count", &print_count_only, "print only number of matches"), OPT_STRING_MULTI( 'd', "define", &ext_vars, MAX_ARGS_EXT_VAR, "define external variable", "VAR=VALUE"), OPT_BOOLEAN(0, "fail-on-warnings", &fail_on_warnings, "fail on warnings"), OPT_BOOLEAN('f', "fast-scan", &fast_scan, "fast matching mode"), OPT_BOOLEAN('h', "help", &show_help, "show this help and exit"), OPT_STRING_MULTI( 'i', "identifier", &identifiers, MAX_ARGS_IDENTIFIER, "print only rules named IDENTIFIER", "IDENTIFIER"), OPT_INTEGER( 'l', "max-rules", &limit, "abort scanning after matching a NUMBER of rules", "NUMBER"), OPT_INTEGER( 0, "max-strings-per-rule", &max_strings_per_rule, "set maximum number of strings per rule (default=10000)", "NUMBER"), OPT_STRING_MULTI( 'x', "module-data", &modules_data, MAX_ARGS_MODULE_DATA, "pass FILE's content as extra data to MODULE", "MODULE=FILE"), OPT_BOOLEAN( 'n', "negate", &negate, "print only not satisfied rules (negate)", NULL), OPT_BOOLEAN('w', "no-warnings", &ignore_warnings, "disable warnings"), OPT_BOOLEAN('m', "print-meta", &show_meta, "print metadata"), OPT_BOOLEAN( 'D', "print-module-data", &show_module_data, "print module data"), OPT_BOOLEAN( 'e', "print-namespace", &show_namespace, "print rules' namespace"), OPT_BOOLEAN('S', "print-stats", &show_stats, "print rules' statistics"), OPT_BOOLEAN('s', "print-strings", &show_strings, "print matching strings"), OPT_BOOLEAN( 'L', "print-string-length", &show_string_length, "print length of matched strings"), OPT_BOOLEAN('g', "print-tags", &show_tags, "print tags"), OPT_BOOLEAN( 'r', "recursive", &recursive_search, "recursively search directories"), OPT_BOOLEAN( 'N', "no-follow-symlinks", &follow_symlinks, "do not follow symlinks when scanning"), OPT_BOOLEAN( 0, "scan-list", &scan_list_search, "scan files listed in FILE, one per line"), OPT_INTEGER( 'k', "stack-size", &stack_size, "set maximum stack size (default=16384)", "SLOTS"), OPT_STRING_MULTI( 't', "tag", &tags, MAX_ARGS_TAG, "print only rules tagged as TAG", "TAG"), OPT_INTEGER( 'p', "threads", &threads, "use the specified NUMBER of threads to scan a directory", "NUMBER"), OPT_INTEGER( 'a', "timeout", &timeout, "abort scanning after the given number of SECONDS", "SECONDS"), OPT_BOOLEAN('v', "version", &show_version, "show version information"), OPT_END()}; // file_queue is size-limited queue stored as a circular array, files are // removed from queue_head position and new files are added at queue_tail // position. The array has room for one extra element to avoid queue_head // being equal to queue_tail in a full queue. The only situation where // queue_head == queue_tail is when queue is empty. QUEUED_FILE file_queue[MAX_QUEUED_FILES + 1]; int queue_head; int queue_tail; SEMAPHORE used_slots; SEMAPHORE unused_slots; MUTEX queue_mutex; MUTEX output_mutex; MODULE_DATA* modules_data_list = NULL; static int file_queue_init() { int result; queue_tail = 0; queue_head = 0; result = cli_mutex_init(&queue_mutex); if (result != 0) return result; result = cli_semaphore_init(&used_slots, 0); if (result != 0) return result; return cli_semaphore_init(&unused_slots, MAX_QUEUED_FILES); } static void file_queue_destroy() { cli_mutex_destroy(&queue_mutex); cli_semaphore_destroy(&unused_slots); cli_semaphore_destroy(&used_slots); } static void file_queue_finish() { for (int i = 0; i < YR_MAX_THREADS; i++) cli_semaphore_release(&used_slots); } static int file_queue_put(const char* file_path, time_t deadline) { if (cli_semaphore_wait(&unused_slots, deadline) == ERROR_SCAN_TIMEOUT) return ERROR_SCAN_TIMEOUT; cli_mutex_lock(&queue_mutex); file_queue[queue_tail].path = strdup(file_path); queue_tail = (queue_tail + 1) % (MAX_QUEUED_FILES + 1); cli_mutex_unlock(&queue_mutex); cli_semaphore_release(&used_slots); return ERROR_SUCCESS; } static char* file_queue_get(time_t deadline) { char* result; if (cli_semaphore_wait(&used_slots, deadline) == ERROR_SCAN_TIMEOUT) return NULL; cli_mutex_lock(&queue_mutex); if (queue_head == queue_tail) // queue is empty { result = NULL; } else { result = file_queue[queue_head].path; queue_head = (queue_head + 1) % (MAX_QUEUED_FILES + 1); } cli_mutex_unlock(&queue_mutex); cli_semaphore_release(&unused_slots); return result; } #if defined(_WIN32) || defined(__CYGWIN__) static bool is_directory(const char* path) { DWORD attributes = GetFileAttributes(path); if (attributes != INVALID_FILE_ATTRIBUTES && attributes & FILE_ATTRIBUTE_DIRECTORY) return true; else return false; } static int scan_dir(const char* dir, SCAN_OPTIONS* scan_opts) { int result = ERROR_SUCCESS; static char path_and_mask[MAX_PATH]; snprintf(path_and_mask, sizeof(path_and_mask), "%s\\*", dir); WIN32_FIND_DATA FindFileData; HANDLE hFind = FindFirstFile(path_and_mask, &FindFileData); if (hFind != INVALID_HANDLE_VALUE) { do { char full_path[MAX_PATH]; snprintf( full_path, sizeof(full_path), "%s\\%s", dir, FindFileData.cFileName); if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { result = file_queue_put(full_path, scan_opts->deadline); } else if ( scan_opts->recursive_search && strcmp(FindFileData.cFileName, ".") != 0 && strcmp(FindFileData.cFileName, "..") != 0) { result = scan_dir(full_path, scan_opts); } } while (result != ERROR_SCAN_TIMEOUT && FindNextFile(hFind, &FindFileData)); FindClose(hFind); } return result; } static int populate_scan_list(const char* filename, SCAN_OPTIONS* scan_opts) { DWORD nread; int result = ERROR_SUCCESS; HANDLE hFile = CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { fprintf(stderr, "error: could not open file \"%s\".\n", filename); return ERROR_COULD_NOT_OPEN_FILE; } DWORD fileSize = GetFileSize(hFile, NULL); if (fileSize == INVALID_FILE_SIZE) { fprintf( stderr, "error: could not determine size of file \"%s\".\n", filename); CloseHandle(hFile); return ERROR_COULD_NOT_READ_FILE; } // INVALID_FILE_SIZE is 0xFFFFFFFF, so (+1) will not overflow char* buf = (char*) VirtualAlloc( NULL, fileSize + 1, MEM_COMMIT, PAGE_READWRITE); if (buf == NULL) { fprintf( stderr, "error: could not allocate memory for file \"%s\".\n", filename); CloseHandle(hFile); return ERROR_INSUFFICIENT_MEMORY; } DWORD total = 0; while (total < fileSize) { if (!ReadFile(hFile, buf + total, fileSize - total, &nread, NULL)) { fprintf(stderr, "error: could not read file \"%s\".\n", filename); CloseHandle(hFile); return ERROR_COULD_NOT_READ_FILE; } total += nread; } // Note: There's no need for reentrant strtok variants since this // function is run in single-threaded code. char* path = strtok(buf, "\n"); while (result != ERROR_SCAN_TIMEOUT && path != NULL) { // Remove trailing carriage return, if present. if (*path != '\0') { char* final = path + strlen(path) - 1; if (*final == '\r') *final = '\0'; } if (is_directory(path)) result = scan_dir(path, scan_opts); else result = file_queue_put(path, scan_opts->deadline); path = strtok(NULL, "\n"); } CloseHandle(hFile); return result; } #else static bool is_directory(const char* path) { struct stat st; if (stat(path, &st) == 0) return S_ISDIR(st.st_mode); return 0; } static int scan_dir(const char* dir, SCAN_OPTIONS* scan_opts) { int result = ERROR_SUCCESS; DIR* dp = opendir(dir); if (dp) { struct dirent* de = readdir(dp); while (de && result != ERROR_SCAN_TIMEOUT) { char full_path[MAX_PATH]; struct stat st; snprintf(full_path, sizeof(full_path), "%s/%s", dir, de->d_name); int err = lstat(full_path, &st); // If lstat returned error, or this directory entry is a symlink and the // user doesn't want to follow symlinks, skip the entry and continue with // the next one. if (err != 0 || (S_ISLNK(st.st_mode) && !scan_opts->follow_symlinks)) { de = readdir(dp); continue; } // If the directory entry is a symlink, check if it points to . and // skip it in that case. else if (S_ISLNK(st.st_mode)) { char buf[2]; int len = readlink(full_path, buf, sizeof(buf)); if (len == 1 && strcmp(buf, ".") == 0) { de = readdir(dp); continue; } } err = stat(full_path, &st); if (err == 0) { if (S_ISREG(st.st_mode)) { result = file_queue_put(full_path, scan_opts->deadline); } else if ( scan_opts->recursive_search && S_ISDIR(st.st_mode) && strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0) { result = scan_dir(full_path, scan_opts); } } de = readdir(dp); } closedir(dp); } return result; } static int populate_scan_list(const char* filename, SCAN_OPTIONS* scan_opts) { size_t nsize = 0; ssize_t nread; char* path = NULL; int result = ERROR_SUCCESS; FILE* fh_scan_list = fopen(filename, "r"); if (fh_scan_list == NULL) { fprintf(stderr, "error: could not open file \"%s\".\n", filename); return ERROR_COULD_NOT_OPEN_FILE; } while (result != ERROR_SCAN_TIMEOUT && (nread = getline(&path, &nsize, fh_scan_list)) != -1) { // remove trailing newline if (nread && path[nread - 1] == '\n') { path[nread - 1] = '\0'; nread--; } if (is_directory(path)) result = scan_dir(path, scan_opts); else result = file_queue_put(path, scan_opts->deadline); } free(path); fclose(fh_scan_list); return result; } #endif static void print_string(const uint8_t* data, int length) { for (int i = 0; i < length; i++) { if (data[i] >= 32 && data[i] <= 126) printf("%c", data[i]); else printf("\\x%02X", data[i]); } printf("\n"); } static char cescapes[] = { 0, 0, 0, 0, 0, 0, 0, 'a', 'b', 't', 'n', 'v', 'f', 'r', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static void print_escaped(const uint8_t* data, size_t length) { for (size_t i = 0; i < length; i++) { switch (data[i]) { case '\"': case '\'': case '\\': printf("\\%c", data[i]); break; default: if (data[i] >= 127) printf("\\%03o", data[i]); else if (data[i] >= 32) putchar(data[i]); else if (cescapes[data[i]] != 0) printf("\\%c", cescapes[data[i]]); else printf("\\%03o", data[i]); } } } static void print_hex_string(const uint8_t* data, int length) { for (int i = 0; i < min(64, length); i++) printf("%s%02X", (i == 0 ? "" : " "), data[i]); puts(length > 64 ? " ..." : ""); } static void print_error(int error) { switch (error) { case ERROR_SUCCESS: break; case ERROR_COULD_NOT_ATTACH_TO_PROCESS: fprintf(stderr, "can not attach to process (try running as root)\n"); break; case ERROR_INSUFFICIENT_MEMORY: fprintf(stderr, "not enough memory\n"); break; case ERROR_SCAN_TIMEOUT: fprintf(stderr, "scanning timed out\n"); break; case ERROR_COULD_NOT_OPEN_FILE: fprintf(stderr, "could not open file\n"); break; case ERROR_UNSUPPORTED_FILE_VERSION: fprintf(stderr, "rules were compiled with a different version of YARA\n"); break; case ERROR_INVALID_FILE: fprintf(stderr, "invalid compiled rules file.\n"); break; case ERROR_CORRUPT_FILE: fprintf(stderr, "corrupt compiled rules file.\n"); break; case ERROR_EXEC_STACK_OVERFLOW: fprintf( stderr, "stack overflow while evaluating condition " "(see --stack-size argument) \n"); break; case ERROR_INVALID_EXTERNAL_VARIABLE_TYPE: fprintf(stderr, "invalid type for external variable\n"); break; case ERROR_TOO_MANY_MATCHES: fprintf(stderr, "too many matches\n"); break; default: fprintf(stderr, "error: %d\n", error); break; } } static void print_scanner_error(YR_SCANNER* scanner, int error) { YR_RULE* rule = yr_scanner_last_error_rule(scanner); YR_STRING* string = yr_scanner_last_error_string(scanner); if (rule != NULL && string != NULL) { fprintf( stderr, "string \"%s\" in rule \"%s\" caused ", string->identifier, rule->identifier); } else if (rule != NULL) { fprintf(stderr, "rule \"%s\" caused ", rule->identifier); } print_error(error); } static void print_compiler_error( int error_level, const char* file_name, int line_number, const YR_RULE* rule, const char* message, void* user_data) { char* msg_type; if (error_level == YARA_ERROR_LEVEL_ERROR) { msg_type = "error"; } else if (!ignore_warnings) { COMPILER_RESULTS* compiler_results = (COMPILER_RESULTS*) user_data; compiler_results->warnings++; msg_type = "warning"; } else { return; } if (rule != NULL) { fprintf( stderr, "%s: rule \"%s\" in %s(%d): %s\n", msg_type, rule->identifier, file_name, line_number, message); } else { fprintf( stderr, "%s(%d): %s: %s\n", file_name, line_number, msg_type, message); } } static void print_rules_stats(YR_RULES* rules) { YR_RULES_STATS stats; int t = sizeof(stats.top_ac_match_list_lengths) / sizeof(stats.top_ac_match_list_lengths[0]); int result = yr_rules_get_stats(rules, &stats); if (result != ERROR_SUCCESS) { print_error(result); return; } printf("size of AC transition table : %d\n", stats.ac_tables_size); printf( "average length of AC matches lists : %f\n", stats.ac_average_match_list_length); printf("number of rules : %d\n", stats.num_rules); printf("number of strings : %d\n", stats.num_strings); printf("number of AC matches : %d\n", stats.ac_matches); printf( "number of AC matches in root node : %d\n", stats.ac_root_match_list_length); printf("number of AC matches in top %d longest lists\n", t); for (int i = 0; i < t; i++) printf(" %3d: %d\n", i + 1, stats.top_ac_match_list_lengths[i]); printf("match list length percentiles\n"); for (int i = 100; i >= 0; i--) printf(" %3d: %d\n", i, stats.ac_match_list_length_pctls[i]); } static int handle_message( YR_SCAN_CONTEXT* context, int message, YR_RULE* rule, void* data) { const char* tag; bool show = true; if (tags[0] != NULL) { // The user specified one or more -t arguments, let's show this rule // only if it's tagged with some of the specified tags. show = false; for (int i = 0; !show && tags[i] != NULL; i++) { yr_rule_tags_foreach(rule, tag) { if (strcmp(tag, tags[i]) == 0) { show = true; break; } } } } if (identifiers[0] != NULL) { // The user specified one or more -i arguments, let's show // this rule only if it's identifier is among of the provided ones. show = false; for (int i = 0; !show && identifiers[i] != NULL; i++) { if (strcmp(identifiers[i], rule->identifier) == 0) { show = true; break; } } } bool is_matching = (message == CALLBACK_MSG_RULE_MATCHING); show = show && ((!negate && is_matching) || (negate && !is_matching)); if (show && !print_count_only) { cli_mutex_lock(&output_mutex); if (show_namespace) printf("%s:", rule->ns->name); printf("%s ", rule->identifier); if (show_tags) { printf("["); yr_rule_tags_foreach(rule, tag) { // print a comma except for the first tag if (tag != rule->tags) printf(","); printf("%s", tag); } printf("] "); } // Show meta-data. if (show_meta) { YR_META* meta; printf("["); yr_rule_metas_foreach(rule, meta) { if (meta != rule->metas) printf(","); if (meta->type == META_TYPE_INTEGER) { printf("%s=%" PRId64, meta->identifier, meta->integer); } else if (meta->type == META_TYPE_BOOLEAN) { printf("%s=%s", meta->identifier, meta->integer ? "true" : "false"); } else { printf("%s=\"", meta->identifier); print_escaped((uint8_t*) (meta->string), strlen(meta->string)); putchar('"'); } } printf("] "); } printf("%s\n", ((CALLBACK_ARGS*) data)->file_path); // Show matched strings. if (show_strings || show_string_length) { YR_STRING* string; yr_rule_strings_foreach(rule, string) { YR_MATCH* match; yr_string_matches_foreach(context, string, match) { if (show_string_length) printf( "0x%" PRIx64 ":%d:%s", match->base + match->offset, match->data_length, string->identifier); else printf( "0x%" PRIx64 ":%s", match->base + match->offset, string->identifier); if (show_strings) { printf(": "); if (STRING_IS_HEX(string)) print_hex_string(match->data, match->data_length); else print_string(match->data, match->data_length); } else { printf("\n"); } } } } cli_mutex_unlock(&output_mutex); } if (is_matching) { ((CALLBACK_ARGS*) data)->current_count++; total_count++; } if (limit != 0 && total_count >= limit) return CALLBACK_ABORT; return CALLBACK_CONTINUE; } static int callback( YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) { YR_MODULE_IMPORT* mi; YR_STRING* string; YR_RULE* rule; YR_OBJECT* object; MODULE_DATA* module_data; switch (message) { case CALLBACK_MSG_RULE_MATCHING: case CALLBACK_MSG_RULE_NOT_MATCHING: return handle_message(context, message, (YR_RULE*) message_data, user_data); case CALLBACK_MSG_IMPORT_MODULE: mi = (YR_MODULE_IMPORT*) message_data; module_data = modules_data_list; while (module_data != NULL) { if (strcmp(module_data->module_name, mi->module_name) == 0) { mi->module_data = (void*) module_data->mapped_file.data; mi->module_data_size = module_data->mapped_file.size; break; } module_data = module_data->next; } return CALLBACK_CONTINUE; case CALLBACK_MSG_MODULE_IMPORTED: if (show_module_data) { object = (YR_OBJECT*) message_data; cli_mutex_lock(&output_mutex); yr_object_print_data(object, 0, 1); printf("\n"); cli_mutex_unlock(&output_mutex); } return CALLBACK_CONTINUE; case CALLBACK_MSG_TOO_MANY_MATCHES: if (ignore_warnings) return CALLBACK_CONTINUE; string = (YR_STRING*) message_data; rule = &context->rules->rules_table[string->rule_idx]; fprintf( stderr, "warning: rule \"%s\": too many matches for %s, results for this rule " "may be incorrect\n", rule->identifier, string->identifier); if (fail_on_warnings) return CALLBACK_ERROR; return CALLBACK_CONTINUE; } return CALLBACK_ERROR; } #if defined(_WIN32) || defined(__CYGWIN__) static DWORD WINAPI scanning_thread(LPVOID param) #else static void* scanning_thread(void* param) #endif { int result = ERROR_SUCCESS; THREAD_ARGS* args = (THREAD_ARGS*) param; char* file_path = file_queue_get(args->deadline); while (file_path != NULL) { args->callback_args.current_count = 0; args->callback_args.file_path = file_path; time_t current_time = time(NULL); if (current_time < args->deadline) { yr_scanner_set_timeout(args->scanner, args->deadline - current_time); result = yr_scanner_scan_file(args->scanner, file_path); if (print_count_only) { cli_mutex_lock(&output_mutex); printf("%s: %d\n", file_path, args->callback_args.current_count); cli_mutex_unlock(&output_mutex); } if (result != ERROR_SUCCESS) { cli_mutex_lock(&output_mutex); fprintf(stderr, "error scanning %s: ", file_path); print_scanner_error(args->scanner, result); cli_mutex_unlock(&output_mutex); } free(file_path); file_path = file_queue_get(args->deadline); } else { file_path = NULL; } } return 0; } static int define_external_variables(YR_RULES* rules, YR_COMPILER* compiler) { int result = ERROR_SUCCESS; for (int i = 0; ext_vars[i] != NULL; i++) { char* equal_sign = strchr(ext_vars[i], '='); if (!equal_sign) { fprintf(stderr, "error: wrong syntax for `-d` option.\n"); return ERROR_SUCCESS; } // Replace the equal sign with null character to split the external // variable definition (i.e: myvar=somevalue) in two strings: identifier // and value. *equal_sign = '\0'; char* identifier = ext_vars[i]; char* value = equal_sign + 1; if (is_float(value)) { if (rules != NULL) result = yr_rules_define_float_variable(rules, identifier, atof(value)); if (compiler != NULL) result = yr_compiler_define_float_variable( compiler, identifier, atof(value)); } else if (is_integer(value)) { if (rules != NULL) result = yr_rules_define_integer_variable( rules, identifier, atoi(value)); if (compiler != NULL) result = yr_compiler_define_integer_variable( compiler, identifier, atoi(value)); } else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0) { if (rules != NULL) result = yr_rules_define_boolean_variable( rules, identifier, strcmp(value, "true") == 0); if (compiler != NULL) result = yr_compiler_define_boolean_variable( compiler, identifier, strcmp(value, "true") == 0); } else { if (rules != NULL) result = yr_rules_define_string_variable(rules, identifier, value); if (compiler != NULL) result = yr_compiler_define_string_variable( compiler, identifier, value); } } return result; } static int load_modules_data() { for (int i = 0; modules_data[i] != NULL; i++) { char* equal_sign = strchr(modules_data[i], '='); if (!equal_sign) { fprintf(stderr, "error: wrong syntax for `-x` option.\n"); return false; } *equal_sign = '\0'; MODULE_DATA* module_data = (MODULE_DATA*) malloc(sizeof(MODULE_DATA)); if (module_data != NULL) { module_data->module_name = modules_data[i]; int result = yr_filemap_map(equal_sign + 1, &module_data->mapped_file); if (result != ERROR_SUCCESS) { free(module_data); fprintf(stderr, "error: could not open file \"%s\".\n", equal_sign + 1); return false; } module_data->next = modules_data_list; modules_data_list = module_data; } } return true; } static void unload_modules_data() { MODULE_DATA* module_data = modules_data_list; while (module_data != NULL) { MODULE_DATA* next_module_data = module_data->next; yr_filemap_unmap(&module_data->mapped_file); free(module_data); module_data = next_module_data; } modules_data_list = NULL; } int main(int argc, const char** argv) { COMPILER_RESULTS cr; YR_COMPILER* compiler = NULL; YR_RULES* rules = NULL; YR_SCANNER* scanner = NULL; SCAN_OPTIONS scan_opts; bool arg_is_dir = false; int flags = 0; int result; argc = args_parse(options, argc, argv); scan_opts.follow_symlinks = follow_symlinks; scan_opts.recursive_search = recursive_search; if (show_version) { printf("%s\n", YR_VERSION); return EXIT_SUCCESS; } if (show_help) { printf( "YARA %s, the pattern matching swiss army knife.\n" "%s\n\n" "Mandatory arguments to long options are mandatory for " "short options too.\n\n", YR_VERSION, USAGE_STRING); args_print_usage(options, 40); printf( "\nSend bug reports and suggestions to: vmalvarez@virustotal.com.\n"); return EXIT_SUCCESS; } if (threads > YR_MAX_THREADS) { fprintf(stderr, "maximum number of threads is %d\n", YR_MAX_THREADS); return EXIT_FAILURE; } if (argc < 2) { // After parsing the command-line options we expect two additional // arguments, the rules file and the target file, directory or pid to // be scanned. fprintf(stderr, "yara: wrong number of arguments\n"); fprintf(stderr, "%s\n\n", USAGE_STRING); fprintf(stderr, "Try `--help` for more options\n"); return EXIT_FAILURE; } if (!load_modules_data()) exit_with_code(EXIT_FAILURE); result = yr_initialize(); if (result != ERROR_SUCCESS) { fprintf(stderr, "error: initialization error (%d)\n", result); exit_with_code(EXIT_FAILURE); } yr_set_configuration(YR_CONFIG_STACK_SIZE, &stack_size); yr_set_configuration(YR_CONFIG_MAX_STRINGS_PER_RULE, &max_strings_per_rule); // Try to load the rules file as a binary file containing // compiled rules first if (rules_are_compiled) { // When a binary file containing compiled rules is provided, yara accepts // only two arguments, the compiled rules file and the target to be scanned. if (argc != 2) { fprintf( stderr, "error: can't accept multiple rules files if one of them is in " "compiled form.\n"); exit_with_code(EXIT_FAILURE); } result = yr_rules_load(argv[0], &rules); if (result == ERROR_SUCCESS) result = define_external_variables(rules, NULL); } else { // Rules file didn't contain compiled rules, let's handle it // as a text file containing rules in source form. if (yr_compiler_create(&compiler) != ERROR_SUCCESS) exit_with_code(EXIT_FAILURE); result = define_external_variables(NULL, compiler); if (result != ERROR_SUCCESS) { print_error(result); exit_with_code(EXIT_FAILURE); } if (atom_quality_table != NULL) { result = yr_compiler_load_atom_quality_table( compiler, atom_quality_table, 0); if (result != ERROR_SUCCESS) { fprintf(stderr, "error loading atom quality table: "); print_error(result); exit_with_code(EXIT_FAILURE); } } cr.errors = 0; cr.warnings = 0; yr_compiler_set_callback(compiler, print_compiler_error, &cr); if (!compile_files(compiler, argc, argv)) exit_with_code(EXIT_FAILURE); if (cr.errors > 0) exit_with_code(EXIT_FAILURE); if (fail_on_warnings && cr.warnings > 0) exit_with_code(EXIT_FAILURE); result = yr_compiler_get_rules(compiler, &rules); yr_compiler_destroy(compiler); compiler = NULL; } if (result != ERROR_SUCCESS) { print_error(result); exit_with_code(EXIT_FAILURE); } if (show_stats) print_rules_stats(rules); cli_mutex_init(&output_mutex); if (fast_scan) flags |= SCAN_FLAGS_FAST_MODE; scan_opts.deadline = time(NULL) + timeout; arg_is_dir = is_directory(argv[argc - 1]); if (scan_list_search && arg_is_dir) { fprintf(stderr, "error: cannot use a directory as scan list.\n"); exit_with_code(EXIT_FAILURE); } else if (scan_list_search || arg_is_dir) { if (file_queue_init() != 0) { print_error(ERROR_INTERNAL_FATAL_ERROR); exit_with_code(EXIT_FAILURE); } THREAD thread[YR_MAX_THREADS]; THREAD_ARGS thread_args[YR_MAX_THREADS]; for (int i = 0; i < threads; i++) { thread_args[i].deadline = scan_opts.deadline; thread_args[i].current_count = 0; result = yr_scanner_create(rules, &thread_args[i].scanner); if (result != ERROR_SUCCESS) { print_error(result); exit_with_code(EXIT_FAILURE); } yr_scanner_set_callback( thread_args[i].scanner, callback, &thread_args[i].callback_args); yr_scanner_set_flags(thread_args[i].scanner, flags); if (cli_create_thread( &thread[i], scanning_thread, (void*) &thread_args[i])) { print_error(ERROR_COULD_NOT_CREATE_THREAD); exit_with_code(EXIT_FAILURE); } } if (arg_is_dir) { scan_dir(argv[argc - 1], &scan_opts); } else { result = populate_scan_list(argv[argc - 1], &scan_opts); if (result != ERROR_SUCCESS) exit_with_code(EXIT_FAILURE); } file_queue_finish(); // Wait for scan threads to finish for (int i = 0; i < threads; i++) cli_thread_join(&thread[i]); for (int i = 0; i < threads; i++) yr_scanner_destroy(thread_args[i].scanner); file_queue_destroy(); } else { CALLBACK_ARGS user_data = {argv[argc - 1], 0}; result = yr_scanner_create(rules, &scanner); if (result != ERROR_SUCCESS) { fprintf(stderr, "error: %d\n", result); exit_with_code(EXIT_FAILURE); } yr_scanner_set_callback(scanner, callback, &user_data); yr_scanner_set_flags(scanner, flags); yr_scanner_set_timeout(scanner, timeout); if (is_integer(argv[argc - 1])) result = yr_scanner_scan_proc(scanner, atoi(argv[argc - 1])); else result = yr_scanner_scan_file(scanner, argv[argc - 1]); if (result != ERROR_SUCCESS) { fprintf(stderr, "error scanning %s: ", argv[argc - 1]); print_scanner_error(scanner, result); exit_with_code(EXIT_FAILURE); } if (print_count_only) printf("%d\n", user_data.current_count); #ifdef YR_PROFILING_ENABLED yr_scanner_print_profiling_info(scanner); #endif } result = EXIT_SUCCESS; _exit: unload_modules_data(); if (scanner != NULL) yr_scanner_destroy(scanner); if (compiler != NULL) yr_compiler_destroy(compiler); if (rules != NULL) yr_rules_destroy(rules); yr_finalize(); return result; } yara-4.1.3/cli/yarac.c000066400000000000000000000157421413423160300144730ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _WIN32 #include #include #include #include #else #include #endif #include #include #include #include #include #include "args.h" #include "common.h" #ifndef MAX_PATH #define MAX_PATH 256 #endif #define MAX_ARGS_EXT_VAR 32 typedef struct COMPILER_RESULTS { int errors; int warnings; } COMPILER_RESULTS; static char* atom_quality_table; static char* ext_vars[MAX_ARGS_EXT_VAR + 1]; static bool ignore_warnings = false; static bool show_version = false; static bool show_help = false; static bool fail_on_warnings = false; static int max_strings_per_rule = DEFAULT_MAX_STRINGS_PER_RULE; #define USAGE_STRING \ "Usage: yarac [OPTION]... [NAMESPACE:]SOURCE_FILE... OUTPUT_FILE" args_option_t options[] = { OPT_STRING( 0, "atom-quality-table", &atom_quality_table, "path to a file with the atom quality table", "FILE"), OPT_STRING_MULTI( 'd', "define", &ext_vars, MAX_ARGS_EXT_VAR, "define external variable", "VAR=VALUE"), OPT_BOOLEAN(0, "fail-on-warnings", &fail_on_warnings, "fail on warnings"), OPT_BOOLEAN('h', "help", &show_help, "show this help and exit"), OPT_INTEGER( 0, "max-strings-per-rule", &max_strings_per_rule, "set maximum number of strings per rule (default=10000)", "NUMBER"), OPT_BOOLEAN('w', "no-warnings", &ignore_warnings, "disable warnings"), OPT_BOOLEAN('v', "version", &show_version, "show version information"), OPT_END()}; static void report_error( int error_level, const char* file_name, int line_number, const YR_RULE* rule, const char* message, void* user_data) { char* msg_type; if (error_level == YARA_ERROR_LEVEL_ERROR) { msg_type = "error"; } else if (!ignore_warnings) { COMPILER_RESULTS* compiler_results = (COMPILER_RESULTS*) user_data; compiler_results->warnings++; msg_type = "warning"; } else { return; } if (rule != NULL) { fprintf( stderr, "%s: rule \"%s\" in %s(%d): %s\n", msg_type, rule->identifier, file_name, line_number, message); } else { fprintf( stderr, "%s: %s(%d): %s\n", msg_type, file_name, line_number, message); } } static bool define_external_variables(YR_COMPILER* compiler) { for (int i = 0; ext_vars[i] != NULL; i++) { char* equal_sign = strchr(ext_vars[i], '='); if (!equal_sign) { fprintf(stderr, "error: wrong syntax for `-d` option.\n"); return false; } // Replace the equal sign with null character to split the external // variable definition (i.e: myvar=somevalue) in two strings: identifier // and value. *equal_sign = '\0'; char* identifier = ext_vars[i]; char* value = equal_sign + 1; if (is_float(value)) { yr_compiler_define_float_variable(compiler, identifier, atof(value)); } else if (is_integer(value)) { yr_compiler_define_integer_variable(compiler, identifier, atoi(value)); } else if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0) { yr_compiler_define_boolean_variable( compiler, identifier, strcmp(value, "true") == 0); } else { yr_compiler_define_string_variable(compiler, identifier, value); } } return true; } int main(int argc, const char** argv) { COMPILER_RESULTS cr; YR_COMPILER* compiler = NULL; YR_RULES* rules = NULL; int result; argc = args_parse(options, argc, argv); if (show_version) { printf("%s\n", YR_VERSION); return EXIT_SUCCESS; } if (show_help) { printf("%s\n\n", USAGE_STRING); args_print_usage(options, 40); printf("\nSend bug reports and suggestions to: vmalvarez@virustotal.com\n"); return EXIT_SUCCESS; } if (argc < 2) { fprintf(stderr, "yarac: wrong number of arguments\n"); fprintf(stderr, "%s\n\n", USAGE_STRING); fprintf(stderr, "Try `--help` for more options\n"); exit_with_code(EXIT_FAILURE); } result = yr_initialize(); if (result != ERROR_SUCCESS) exit_with_code(EXIT_FAILURE); if (yr_compiler_create(&compiler) != ERROR_SUCCESS) exit_with_code(EXIT_FAILURE); if (!define_external_variables(compiler)) exit_with_code(EXIT_FAILURE); if (atom_quality_table != NULL) { result = yr_compiler_load_atom_quality_table( compiler, atom_quality_table, 0); if (result != ERROR_SUCCESS) { fprintf(stderr, "error loading atom quality table\n"); exit_with_code(EXIT_FAILURE); } } cr.errors = 0; cr.warnings = 0; yr_set_configuration(YR_CONFIG_MAX_STRINGS_PER_RULE, &max_strings_per_rule); yr_compiler_set_callback(compiler, report_error, &cr); if (!compile_files(compiler, argc, argv)) exit_with_code(EXIT_FAILURE); if (cr.errors > 0) exit_with_code(EXIT_FAILURE); if (fail_on_warnings && cr.warnings > 0) exit_with_code(EXIT_FAILURE); result = yr_compiler_get_rules(compiler, &rules); if (result != ERROR_SUCCESS) { fprintf(stderr, "error: %d\n", result); exit_with_code(EXIT_FAILURE); } result = yr_rules_save(rules, argv[argc - 1]); if (result != ERROR_SUCCESS) { fprintf(stderr, "error: %d\n", result); exit_with_code(EXIT_FAILURE); } result = EXIT_SUCCESS; _exit: if (compiler != NULL) yr_compiler_destroy(compiler); if (rules != NULL) yr_rules_destroy(rules); yr_finalize(); return result; } yara-4.1.3/configure.ac000066400000000000000000000275361413423160300147530ustar00rootroot00000000000000AC_INIT([yara], [4.1.3], [vmalvarez@virustotal.com]) AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR([cli/yara.c]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AC_CONFIG_MACRO_DIR([m4]) # AC_PROG_CC sets CFLAGS to "-g -O2" if it wasn't previously set. Let's set # an empty CFLAGS. : ${CFLAGS=""} no_std_allocator="-fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free" # automake 1.12 seems to require AM_PROG_AR, but automake 1.11 doesn't # recognize it. m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) AC_PROG_CC AC_PROG_CC_C99 AM_PROG_CC_C_O AM_PROG_LEX AC_PROG_YACC # If the C compiler supports the keyword inline, do nothing. Otherwise define # inline to __inline__ or __inline if it accepts one of those, otherwise define # inline to be empty. AC_C_INLINE # Defines WORDS_BIGENDIAN if building in a big-endian host. AC_C_BIGENDIAN # Arrange for 64-bit file offsets. AC_SYS_LARGEFILE LT_INIT AC_CANONICAL_HOST case $host_alias in i?86-*-mingw*) CFLAGS="$CFLAGS -D__MINGW_USE_VC2005_COMPAT" ;; esac case $host_os in darwin*) CFLAGS="$CFLAGS -I/opt/local/include" # Starting with Mac OS X 10.11 (El Capitan) the OpenSSL headers # are in /usr/local/opt/openssl/include CFLAGS="$CFLAGS -DUSE_MACH_PROC -I/usr/local/opt/openssl/include" LDFLAGS="$LDFLAGS -L/usr/local/opt/openssl/lib" posix=true proc_interface=mach jemalloc_prefix=je_ ;; mingw*|cygwin*) CFLAGS="$CFLAGS -DUSE_WINDOWS_PROC" proc_interface=windows jemalloc_prefix= ;; linux*|netbsd*|dragonfly*|kfreebsd*) CFLAGS="$CFLAGS -DUSE_LINUX_PROC" posix=true proc_interface=linux jemalloc_prefix= ;; freebsd*) CFLAGS="$CFLAGS -DUSE_FREEBSD_PROC" posix=true proc_interface=freebsd jemalloc_prefix= ;; openbsd*) CFLAGS="$CFLAGS -DUSE_OPENBSD_PROC" CFLAGS="$CFLAGS -I/usr/local/include -L/usr/local/lib" posix=true proc_interface=openbsd jemalloc_prefix= ;; *) CFLAGS="$CFLAGS -DUSE_NO_PROC" proc_interface=none jemalloc_prefix= ;; esac ACX_PTHREAD( [LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" CC="$PTHREAD_CC"], [AC_MSG_ERROR([pthread API support is required.])]) AC_CHECK_LIB(m, isnan) AC_CHECK_LIB(m, log2) AS_IF([test "ac_cv_lib_m_isnan" = yes || test "$ac_cv_lib_m_log2" = yes], [PC_LIBS_PRIVATE="$PC_LIBS_PRIVATE -lm"]) AC_CHECK_FUNCS([strlcpy strlcat memmem timegm _mkgmtime clock_gettime]) AC_CHECK_HEADERS([stdbool.h]) AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [compiles with -g option])], [if test x$enableval = xyes; then debug=true fi]) AC_ARG_ENABLE([gcov], [AS_HELP_STRING([--enable-gcov], [compiles with -O0 -g -ftest-coverage -fprofile-arcs])], [if test x$enableval = xyes; then gcov=true fi]) AC_ARG_ENABLE([optimization], [AS_HELP_STRING([--disable-optimization], [disable compiler optimizations with -O0])], [if test x$enableval = xyes; then optimization=false fi], [optimization=true]) AC_ARG_ENABLE([address-sanitizer], [AS_HELP_STRING([--enable-address-sanitizer], [compiles with -fsanitize=address])], [if test x$enableval = xyes; then address_sanitizer=true fi]) AC_ARG_ENABLE([profiling], [AS_HELP_STRING([--enable-profiling], [enable rules profiling support])], [if test x$enableval = xyes; then profiling_enabled=true CFLAGS="$CFLAGS -DYR_PROFILING_ENABLED" fi]) AC_ARG_ENABLE([cuckoo], [AS_HELP_STRING([--enable-cuckoo], [enable cuckoo module])], [if test x$enableval = xyes; then build_cuckoo_module=true AC_CHECK_HEADERS([jansson.h],, AC_MSG_ERROR([please install Jansson library])) AC_CHECK_LIB(jansson, json_loadb,, AC_MSG_ERROR([please install Jansson library])) CFLAGS="$CFLAGS -DCUCKOO_MODULE" PC_REQUIRES_PRIVATE="$PC_REQUIRES_PRIVATE jansson" fi]) AC_ARG_ENABLE([magic], [AS_HELP_STRING([--enable-magic], [enable magic module])], [if test x$enableval = xyes; then build_magic_module=true AC_CHECK_HEADERS([magic.h],, AC_MSG_ERROR([please install libmagic library])) AC_CHECK_LIB(magic, magic_open,, AC_MSG_ERROR([please install libmagic library])) CFLAGS="$CFLAGS -DMAGIC_MODULE" PC_LIBS_PRIVATE="$PC_LIBS_PRIVATE -lmagic" fi]) AC_ARG_ENABLE([dotnet], [AS_HELP_STRING([--enable-dotnet], [enable dotnet module])], [if test x$enableval = xyes; then build_dotnet_module=true CFLAGS="$CFLAGS -DDOTNET_MODULE" fi]) AC_ARG_ENABLE([macho], [AS_HELP_STRING([--enable-macho], [enable macho module])], [if test x$enableval = xyes; then build_macho_module=true CFLAGS="$CFLAGS -DMACHO_MODULE" fi]) AC_ARG_ENABLE([dex], [AS_HELP_STRING([--enable-dex], [enable dex module])], [if test x$enableval = xyes; then build_dex_module=true CFLAGS="$CFLAGS -DDEX_MODULE" fi]) AC_ARG_ENABLE([debug-dex], [AS_HELP_STRING([--enable-debug-dex], [enable dex module debugging])], [if test x$enableval = xyes; then debug_dex_module=true CFLAGS="$CFLAGS -DDEBUG_DEX_MODULE" fi]) AC_ARG_ENABLE([pb-tests], [AS_HELP_STRING([--enable-pb-tests], [enable protobuf test module])], [if test x$enableval = xyes; then build_pb_tests_module=true AC_CHECK_PROG(PROTOC, protoc, protoc) AS_IF([test "x${PROTOC}" == "x"], [AC_MSG_ERROR([protobuf compiler "protoc" not found])]) # AC_CHECK_PROG(PROTOC_GEN_YARA, protoc-gen-yara, protoc-gen-yara) # AS_IF([test "x${PROTOC_GEN_YARA}" == "x"], # [AC_MSG_ERROR([please install https://github.com/VirusTotal/protoc-gen-yara])]) PKG_CHECK_MODULES(PROTOBUF_C, libprotobuf-c >= 1.0.0) AC_CHECK_LIB(protobuf-c, protobuf_c_message_unpack,, AC_MSG_ERROR([please install libprotobuf-c library])) CFLAGS="$CFLAGS -DPB_TESTS_MODULE" PC_REQUIRES_PRIVATE="$PC_REQUIRES_PRIVATE libprotobuf-c" fi]) AC_ARG_WITH([jemalloc], [AS_HELP_STRING([--with-jemalloc], [use jemalloc to debug heap-related issues])], [if test x$withval = xyes; then mallctl=mallctl malloc_stats_print=malloc_stats_print AC_CHECK_LIB(jemalloc, $jemalloc_prefix$mallctl,, AC_MSG_ERROR([please install jemalloc library])) CFLAGS="$CFLAGS -DJEMALLOC -Dmallctl=$jemalloc_prefix$mallctl -Dmalloc_stats_print=$jemalloc_prefix$malloc_stats_print $no_std_allocator" PC_REQUIRES_PRIVATE="$PC_REQUIRES_PRIVATE jemalloc" fi]) AC_ARG_WITH([tcmalloc], [AS_HELP_STRING([--with-tcmalloc], [use tcmalloc as the default heap allocator])], [if test x$withval = xyes; then if test "x$with_jemalloc" = "xyes"; then AC_MSG_ERROR([Cannot compile with both tcmalloc and jemalloc]) fi AC_CHECK_LIB(tcmalloc, tc_cfree,, AC_MSG_ERROR([please install https://github.com/gperftools/gperftools])) CFLAGS="$CFLAGS -DTCMALLOC $no_std_allocator" PC_REQUIRES_PRIVATE="$PC_REQUIRES_PRIVATE tcmalloc" fi]) AC_ARG_WITH([cpu-profiler], [AS_HELP_STRING([--with-cpu-profiler], [compile with CPU profiling support])], [if test x$withval = xyes; then AC_CHECK_LIB(profiler, ProfilerStart,, AC_MSG_ERROR([please install https://github.com/gperftools/gperftools])) PC_REQUIRES_PRIVATE="$PC_REQUIRES_PRIVATE libprofiler" fi]) AC_ARG_WITH([crypto], AS_HELP_STRING([--without-crypto], [ignore presence of OpenSSL and disable it])) AS_IF([test "x$with_crypto" != "xno"], [ AC_CHECK_HEADERS([openssl/md5.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/sha.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/asn1.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/crypto.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/bio.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/pkcs7.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/x509.h],, [have_crypto=no]) AC_CHECK_HEADERS([openssl/safestack.h],, [have_crypto=no]) AC_CHECK_LIB(crypto, MD5_Init,, [have_crypto=no]) AC_CHECK_LIB(crypto, MD5_Update,, [have_crypto=no]) AC_CHECK_LIB(crypto, MD5_Final,, [have_crypto=no]) AC_CHECK_LIB(crypto, SHA256_Init,, [have_crypto=no]) AC_CHECK_LIB(crypto, SHA256_Update,, [have_crypto=no]) AC_CHECK_LIB(crypto, SHA256_Final,, [have_crypto=no]) ], [ have_crypto=no ]) AS_IF([test "x$have_crypto" = "xno"], [ AS_IF([test "x$with_crypto" = "xyes"], [ AC_MSG_ERROR([please install OpenSSL library]) ], [ AC_MSG_WARN([ ***************************************************************** Could not find OpenSSL library. Some features in "pe" module have been disabled. If you want to enable all features please install OpenSSL and run this script again. ***************************************************************** ]) AC_MSG_CHECKING([for Microsoft Crypto API]) AC_CHECK_HEADERS([wincrypt.h], [ AC_MSG_RESULT([The "hash" module functions will be provided through the Microsoft Crypto API]) # FIXME: Add PC_LIBS_PRIVATE entries? build_hash_module=true ], [], [#include ]) AC_MSG_CHECKING([for MacOSX Common Crypto API]) AC_CHECK_HEADERS([CommonCrypto/CommonCrypto.h], [ AC_MSG_RESULT([ ***************************************************************** As an alternative to OpenSSL the "hash" module functions will be provided through the MacOSX Common Crypto API. ***************************************************************** ]) # FIXME: Add PC_LIBS_PRIVATE entries? build_hash_module=true ]) AS_IF([test x$build_hash_module = xtrue], [ build_hash_module=true CFLAGS="$CFLAGS -DHASH_MODULE" ], [ AC_MSG_WARN([ ***************************************************************** Could not find alternative APIs for hash functions. The "hash" module has been disabled. ***************************************************************** ]) ]) ]) ], [ build_hash_module=true CFLAGS="$CFLAGS -DHASH_MODULE" PC_REQUIRES_PRIVATE="$PC_REQUIRES_PRIVATE libcrypto" ]) AM_CONDITIONAL([GCC], [test "x$GCC" = xyes]) AM_CONDITIONAL([PROTOC], [test "x${PROTOC}" != "x"]) AM_CONDITIONAL([DEBUG], [test x$debug = xtrue]) AM_CONDITIONAL([GCOV], [test x$gcov = xtrue]) AM_CONDITIONAL([POSIX], [test x$posix = xtrue]) AM_CONDITIONAL([PROFILING_ENABLED], [test x$profiling_enabled = xtrue]) AM_CONDITIONAL([OPTIMIZATION], [test x$optimization = xtrue]) AM_CONDITIONAL([ADDRESS_SANITIZER], [test x$address_sanitizer = xtrue]) AM_CONDITIONAL([CUCKOO_MODULE], [test x$build_cuckoo_module = xtrue]) AM_CONDITIONAL([MAGIC_MODULE], [test x$build_magic_module = xtrue]) AM_CONDITIONAL([HASH_MODULE], [test x$build_hash_module = xtrue]) AM_CONDITIONAL([DOTNET_MODULE], [test x$build_dotnet_module = xtrue]) AM_CONDITIONAL([MACHO_MODULE], [test x$build_macho_module = xtrue]) AM_CONDITIONAL([PB_TESTS_MODULE], [test x$build_pb_tests_module = xtrue]) AM_CONDITIONAL([DEX_MODULE], [test x$build_dex_module = xtrue]) AM_CONDITIONAL([DEBUG_DEX_MODULE], [test x$debug_dex_module = xtrue]) AM_CONDITIONAL([USE_WINDOWS_PROC], [test x$proc_interface = xwindows ]) AM_CONDITIONAL([USE_LINUX_PROC], [test x$proc_interface = xlinux ]) AM_CONDITIONAL([USE_FREEBSD_PROC], [test x$proc_interface = xfreebsd ]) AM_CONDITIONAL([USE_OPENBSD_PROC], [test x$proc_interface = xopenbsd ]) AM_CONDITIONAL([USE_MACH_PROC], [test x$proc_interface = xmach ]) AM_CONDITIONAL([USE_NO_PROC], [test x$proc_interface = xnone ]) AS_IF( [test x$proc_interface != xnone],[AC_DEFINE([HAVE_SCAN_PROC_IMPL],[1])], [test x$proc_interface = xnone],[AC_DEFINE([HAVE_SCAN_PROC_IMPL],[0])]) AC_SUBST([PC_REQUIRES_PRIVATE]) AC_SUBST([PC_LIBS_PRIVATE]) AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([libyara/Makefile]) AC_CONFIG_FILES([libyara/yara.pc]) AC_OUTPUT yara-4.1.3/dist/000077500000000000000000000000001413423160300134135ustar00rootroot00000000000000yara-4.1.3/dist/yara-python.spec000066400000000000000000000020351413423160300165420ustar00rootroot00000000000000%define name yara-python %define version 3.2.0 %define unmangled_version 3.2.0 %define release 1 Summary: Python bindings for YARA malware research tool Name: %{name} Version: %{version} Release: %{release} Source0: %{name}-%{unmangled_version}.tar.gz License: Apache License 2.0 Group: Development/Libraries BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Prefix: %{_prefix} Vendor: Victor M. Alvarez BuildRequires: gcc python-devel BuildRequires: libyara-devel %description YARA is a tool aimed at (but not limited to) helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families (or whatever you want to describe) based on textual or binary patterns. %prep %setup -n %{name}-%{unmangled_version} %build env CFLAGS="$RPM_OPT_FLAGS" python setup.py build %install python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES %clean rm -rf $RPM_BUILD_ROOT %files -f INSTALLED_FILES %defattr(-,root,root) yara-4.1.3/dist/yara.spec000066400000000000000000000053031413423160300152240ustar00rootroot00000000000000## ## Copyright (c) 2007-2015. The YARA Authors. All Rights Reserved. ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. ## You may obtain a copy of the License at ## http://www.apache.org/licenses/LICENSE-2.0 ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## Name: yara Version: 3.2.0 Release: 1 License: Apache License 2.0 Summary: A malware identification and classification tool Url: http://plusvic.github.io/yara/ Group: System/Filesystems Source: yara-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: autoconf automake libtool %description YARA is a tool aimed at helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families based on textual or binary patterns contained on samples of those families. %package -n libyara Summary: Library to support the yara malware identification tool Group: System/Libraries %description -n libyara YARA is a tool aimed at helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families based on textual or binary patterns contained on samples of those families. %package -n yara-devel Summary: Development files to support the yara malware identification tool Group: Development/Libraries/C and C++ Requires: libyara = %{version}-%{release} %description -n yara-devel YARA is a tool aimed at helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families based on textual or binary patterns contained on samples of those families. %prep %setup -q %build ./bootstrap.sh ./configure make %install make install DESTDIR=%{buildroot} bindir=%{_bindir} libdir=%{_libdir} includedir=%{_includedir} mandir=%{_mandir} INSTALL="install -p" %post -n libyara -p /sbin/ldconfig %postun -n libyara -p /sbin/ldconfig %files %defattr(-,root,root) %{_bindir}/yara %{_bindir}/yarac %{_mandir}/man1/* %files -n libyara %defattr(-,root,root) %{_libdir}/libyara.so* %{_libdir}/pkgconfig/yara.pc %files -n yara-devel %defattr(-,root,root) %{_includedir}/yara.h %{_includedir}/yara/* %{_libdir}/libyara.a %{_libdir}/libyara.la %changelog * Sat Jan 25 2015 Domingo Kiser 3.2.0-1 Initial Creation. yara-4.1.3/docs/000077500000000000000000000000001413423160300134005ustar00rootroot00000000000000yara-4.1.3/docs/Makefile000066400000000000000000000151421413423160300150430ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/yara.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/yara.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/yara" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/yara" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." yara-4.1.3/docs/capi.rst000066400000000000000000001105661413423160300150570ustar00rootroot00000000000000********* The C API ********* You can integrate YARA into your C/C++ project by using the API provided by the *libyara* library. This API gives you access to every YARA feature and it's the same API used by the command-line tools ``yara`` and ``yarac``. Initializing and finalizing *libyara* ===================================== The first thing your program must do when using *libyara* is initializing the library. This is done by calling the :c:func:`yr_initialize` function. This function allocates any resources needed by the library and initializes internal data structures. Its counterpart is :c:func:`yr_finalize`, which must be called when you are finished using the library. In a multi-threaded program only the main thread must call :c:func:`yr_initialize` and :c:func:`yr_finalize`. No additional work is required from other threads using the library. Compiling rules =============== Before using your rules to scan any data you need to compile them into binary form. For that purpose you'll need a YARA compiler, which can be created with :c:func:`yr_compiler_create`. After being used, the compiler must be destroyed with :c:func:`yr_compiler_destroy`. You can use :c:func:`yr_compiler_add_file`, :c:func:`yr_compiler_add_fd`, or :c:func:`yr_compiler_add_string` to add one or more input sources to be compiler. Both of these functions receive an optional namespace. Rules added under the same namespace behave as if they were contained within the same source file or string, so, rule identifiers must be unique among all the sources sharing a namespace. If the namespace argument is ``NULL`` the rules are put in the *default* namespace. The :c:func:`yr_compiler_add_file`, :c:func:`yr_compiler_add_fd`, and :c:func:`yr_compiler_add_string` functions return the number of errors found in the source code. If the rules are correct they will return 0. If any of these functions return an error the compiler can't used anymore, neither for adding more rules nor getting the compiled rules. For obtaining detailed error information you must set a callback function by using :c:func:`yr_compiler_set_callback` before calling any of the compiling functions. The callback function has the following prototype: .. code-block:: c void callback_function( int error_level, const char* file_name, int line_number, const YR_RULE* rule, const char* message, void* user_data) .. versionchanged:: 4.0.0 Possible values for ``error_level`` are ``YARA_ERROR_LEVEL_ERROR`` and ``YARA_ERROR_LEVEL_WARNING``. The arguments ``file_name`` and ``line_number`` contains the file name and line number where the error or warning occurs. ``file_name`` is the one passed to :c:func:`yr_compiler_add_file` or :c:func:`yr_compiler_add_fd`. It can be ``NULL`` if you passed ``NULL`` or if you're using :c:func:`yr_compiler_add_string`. `rule` is a pointer to the `YR_RULE` structure representing the rule that contained the error, but it can be `NULL` it the error is not contained in a specific rule. The ``user_data`` pointer is the same you passed to :c:func:`yr_compiler_set_callback`. By default, for rules containing references to other files (``include "filename.yara"``), YARA will try to find those files on disk. However, if you want to fetch the imported rules from another source (eg: from a database or remote service), a callback function can be set with :c:func:`yr_compiler_set_include_callback`. This callback receives the following parameters: * ``include_name``: name of the requested file. * ``calling_rule_filename``: the requesting file name (NULL if not a file). * ``calling_rule_namespace``: namespace (NULL if undefined). * ``user_data`` same pointer passed to :c:func:`yr_compiler_set_include_callback`. It should return the requested file's content as a null-terminated string. The memory for this string should be allocated by the callback function. Once it is safe to free the memory used to return the callback's result, the include_free function passed to :c:func:`yr_compiler_set_include_callback` will be called. If the memory does not need to be freed, NULL can be passed as include_free instead. You can completely disable support for includes by setting a NULL callback function with :c:func:`yr_compiler_set_include_callback`. The callback function has the following prototype: .. code-block:: c const char* include_callback( const char* include_name, const char* calling_rule_filename, const char* calling_rule_namespace, void* user_data); The free function has the following prototype: .. code-block:: c void include_free( const char* callback_result_ptr, void* user_data); After you successfully added some sources you can get the compiled rules using the :c:func:`yr_compiler_get_rules` function. You'll get a pointer to a :c:type:`YR_RULES` structure which can be used to scan your data as described in :ref:`scanning-data`. Once :c:func:`yr_compiler_get_rules` is invoked you can not add more sources to the compiler, but you can call :c:func:`yr_compiler_get_rules` multiple times. Each time this function is called it returns a pointer to the same :c:type:`YR_RULES` structure. Notice that this behaviour is new in YARA 4.0.0, in YARA 3.X and 2.X :c:func:`yr_compiler_get_rules` returned a new copy the :c:type:`YR_RULES` structure. Instances of :c:type:`YR_RULES` must be destroyed with :c:func:`yr_rules_destroy`. Defining external variables =========================== If your rules make use of external variables (like in the example below), you must define those variables by using any of the ``yr_compiler_define_XXXX_variable`` functions. Variables must be defined before rules are compiled with ``yr_compiler_add_XXXX`` and they must be defined with a type that matches the context in which the variable is used in the rule, a variable that is used like `my_var == 5` can't be defined as a string variable. While defining external variables with ``yr_compiler_define_XXXX_variable`` you must provide a value for each variable. That value is embedded in the compiled rules and used whenever the variable appears in a rule. However, you can change the value associated to an external variable after the rules has been compiled by using any of the ``yr_rules_define_XXXX_variable`` functions. Saving and retrieving compiled rules ==================================== Compiled rules can be saved to a file and retrieved later by using :c:func:`yr_rules_save` and :c:func:`yr_rules_load`. Rules compiled and saved in one machine can be loaded in another machine as long as they have the same endianness, no matter the operating system or if they are 32-bit or 64-bit systems. However files saved with older versions of YARA may not work with newer versions due to changes in the file layout. You can also save and retrieve your rules to and from generic data streams by using functions :c:func:`yr_rules_save_stream` and :c:func:`yr_rules_load_stream`. These functions receive a pointer to a :c:type:`YR_STREAM` structure, defined as: .. code-block:: c typedef struct _YR_STREAM { void* user_data; YR_STREAM_READ_FUNC read; YR_STREAM_WRITE_FUNC write; } YR_STREAM; You must provide your own implementation for ``read`` and ``write`` functions. The ``read`` function is used by :c:func:`yr_rules_load_stream` to read data from your stream and the ``write`` function is used by :c:func:`yr_rules_save_stream` to write data into your stream. Your ``read`` and ``write`` functions must respond to these prototypes: .. code-block:: c size_t read( void* ptr, size_t size, size_t count, void* user_data); size_t write( const void* ptr, size_t size, size_t count, void* user_data); The ``ptr`` argument is a pointer to the buffer where the ``read`` function should put the read data, or where the ``write`` function will find the data that needs to be written to the stream. In both cases ``size`` is the size of each element being read or written and ``count`` the number of elements. The total size of the data being read or written is ``size`` * ``count``. The ``read`` function must return the number of elements read, the ``write`` function must return the total number of elements written. The ``user_data`` pointer is the same you specified in the :c:type:`YR_STREAM` structure. You can use it to pass arbitrary data to your ``read`` and ``write`` functions. .. _scanning-data: Scanning data ============= Once you have an instance of :c:type:`YR_RULES` you can use it directly with one of the ``yr_rules_scan_XXXX`` functions described below, or create a scanner with :c:func:`yr_scanner_create`. Let's start by discussing the first approach. The :c:type:`YR_RULES` you got from the compiler can be used with :c:func:`yr_rules_scan_file`, :c:func:`yr_rules_scan_fd` or :c:func:`yr_rules_scan_mem` for scanning a file, a file descriptor and a in-memory buffer respectively. The results from the scan are returned to your program via a callback function. The callback has the following prototype: .. code-block:: c int callback_function( YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data); Possible values for ``message`` are:: CALLBACK_MSG_RULE_MATCHING CALLBACK_MSG_RULE_NOT_MATCHING CALLBACK_MSG_SCAN_FINISHED CALLBACK_MSG_IMPORT_MODULE CALLBACK_MSG_MODULE_IMPORTED CALLBACK_MSG_TOO_MANY_MATCHES Your callback function will be called once for each rule with either a ``CALLBACK_MSG_RULE_MATCHING`` or ``CALLBACK_MSG_RULE_NOT_MATCHING`` message, depending if the rule is matching or not. In both cases a pointer to the :c:type:`YR_RULE` structure associated with the rule is passed in the ``message_data`` argument. You just need to perform a typecast from ``void*`` to ``YR_RULE*`` to access the structure. You can control whether or not YARA calls your callback function with ``CALLBACK_MSG_RULE_MATCHING`` and ``CALLBACK_MSG_RULE_NOT_MATCHING`` messages by using the ``SCAN_FLAGS_REPORT_RULES_MATCHING`` and ``SCAN_FLAGS_REPORT_RULES_NOT_MATCHING`` as described later in this section. This callback is also called with the ``CALLBACK_MSG_IMPORT_MODULE`` message. All modules referenced by an ``import`` statement in the rules are imported once for every file being scanned. In this case ``message_data`` points to a :c:type:`YR_MODULE_IMPORT` structure. This structure contains a ``module_name`` field pointing to a null terminated string with the name of the module being imported and two other fields ``module_data`` and ``module_data_size``. These fields are initially set to ``NULL`` and ``0``, but your program can assign a pointer to some arbitrary data to ``module_data`` while setting ``module_data_size`` to the size of the data. This way you can pass additional data to those modules requiring it, like the :ref:`Cuckoo-module` for example. Once a module is imported the callback is called again with the CALLBACK_MSG_MODULE_IMPORTED. When this happens ``message_data`` points to a :c:type:`YR_OBJECT_STRUCTURE` structure. This structure contains all the information provided by the module about the currently scanned file. If during the scan a string hits the maximum number of matches your callback will be called once with the CALLBACK_MSG_TOO_MANY_MATCHES. When this happens, `message_data` is a `YR_STRING*` which points to the string which caused the warning. If your callback returns CALLBACK_CONTINUE the string will be disabled and scanning will continue, otherwise scanning will be halted. Lastly, the callback function is also called with the ``CALLBACK_MSG_SCAN_FINISHED`` message when the scan is finished. In this case ``message_data`` is ``NULL``. Notice that you shouldn't call any of the ``yr_rules_scan_XXXX`` functions from within the callback as those functions are not re-entrant. Your callback function must return one of the following values:: CALLBACK_CONTINUE CALLBACK_ABORT CALLBACK_ERROR If it returns ``CALLBACK_CONTINUE`` YARA will continue normally, ``CALLBACK_ABORT`` will abort the scan but the result from the ``yr_rules_scan_XXXX`` function will be ``ERROR_SUCCESS``. On the other hand ``CALLBACK_ERROR`` will abort the scanning too, but the result from ``yr_rules_scan_XXXX`` will be ``ERROR_CALLBACK_ERROR``. The ``user_data`` argument passed to your callback function is the same you passed ``yr_rules_scan_XXXX``. This pointer is not touched by YARA, it's just a way for your program to pass arbitrary data to the callback function. All ``yr_rules_scan_XXXX`` functions receive a ``flags`` argument that allows to tweak some aspects of the scanning process. The supported flags are the following ones: ``SCAN_FLAGS_FAST_MODE`` ``SCAN_FLAGS_NO_TRYCATCH`` ``SCAN_FLAGS_REPORT_RULES_MATCHING`` ``SCAN_FLAGS_REPORT_RULES_NOT_MATCHING`` The ``SCAN_FLAGS_FAST_MODE`` flag makes the scanning a little faster by avoiding multiple matches of the same string when not necessary. Once the string was found in the file it's subsequently ignored, implying that you'll have a single match for the string, even if it appears multiple times in the scanned data. This flag has the same effect of the ``-f`` command-line option described in :ref:`command-line`. ``SCAN_FLAGS_REPORT_RULES_MATCHING`` and ``SCAN_FLAGS_REPORT_RULES_NOT_MATCHING`` control whether the callback is invoked for rules that are matching or for rules that are not matching respectively. If ``SCAN_FLAGS_REPORT_RULES_MATCHING`` is specified alone, the callback will be called for matching rules with the ``CALLBACK_MSG_RULE_MATCHING`` message but it won't be called for non-matching rules. If ``SCAN_FLAGS_REPORT_RULES_NOT_MATCHING`` is specified alone, the opposite happens, the callback will be called with ``CALLBACK_MSG_RULE_NOT_MATCHING`` messages but not with ``CALLBACK_MSG_RULE_MATCHING`` messages. If both flags are combined together (the default) the callback will be called for both matching and non-matching rules. For backward compatibility, if none of these two flags are specified, the scanner will follow the default behavior. Additionally, ``yr_rules_scan_XXXX`` functions can receive a ``timeout`` argument which forces the scan to abort after the specified number of seconds (approximately). If ``timeout`` is 0 it means no timeout at all. Using a scanner --------------- The ``yr_rules_scan_XXXX`` functions are enough in most cases, but sometimes you may need a fine-grained control over the scanning. In those cases you can create a scanner with :c:func:`yr_scanner_create`. A scanner is simply a wrapper around a :c:type:`YR_RULES` structure that holds additional configuration like external variables without affecting other users of the :c:type:`YR_RULES` structure. A scanner is particularly useful when you want to use the same :c:type:`YR_RULES` with multiple workers (it could be a separate thread, a coroutine, etc) and each worker needs to set different set of values for external variables. In that case you can't use ``yr_rules_define_XXXX_variable`` for setting the values of your external variables, as every worker using the :c:type:`YR_RULES` will be affected by such changes. However each worker can have its own scanner, where the scanners share the same :c:type:`YR_RULES`, and use ``yr_scanner_define_XXXX_variable`` for setting external variables without affecting the rest of the workers. This is a better solution than having a separate :c:type:`YR_RULES` for each worker, as :c:type:`YR_RULES` structures have large memory footprint (specially if you have a lot of rules) while scanners are very lightweight. API reference ============= Data structures --------------- .. c:type:: YR_COMPILER Data structure representing a YARA compiler. .. c:type:: YR_SCAN_CONTEXT Data structure that holds information about an on-going scan. A pointer to this structure is passed to the callback function that receives notifications about matches found. This structure is also used for iterating over the .. c:type:: YR_MATCH Data structure representing a string match. .. c:member:: int64_t base Base offset/address for the match. While scanning a file this field is usually zero, while scanning a process memory space this field is the virtual address of the memory block where the match was found. .. c:member:: int64_t offset Offset of the match relative to *base*. .. c:member:: int32_t match_length Length of the matching string .. c:member:: const uint8_t* data Pointer to a buffer containing a portion of the matching string. .. c:member:: int32_t data_length Length of ``data`` buffer. ``data_length`` is the minimum of ``match_length`` and ``MAX_MATCH_DATA``. .. versionchanged:: 3.5.0 .. c:type:: YR_META Data structure representing a metadata value. .. c:member:: const char* identifier Meta identifier. .. c:member:: int32_t type One of the following metadata types: ``META_TYPE_INTEGER`` ``META_TYPE_STRING`` ``META_TYPE_BOOLEAN`` .. c:type:: YR_MODULE_IMPORT .. c:member:: const char* module_name Name of the module being imported. .. c:member:: void* module_data Pointer to additional data passed to the module. Initially set to ``NULL``, your program is responsible for setting this pointer while handling the CALLBACK_MSG_IMPORT_MODULE message. .. c:member:: size_t module_data_size Size of additional data passed to module. Your program must set the appropriate value if ``module_data`` is modified. .. c:type:: YR_RULE Data structure representing a single rule. .. c:member:: const char* identifier Rule identifier. .. c:member:: const char* tags Pointer to a sequence of null terminated strings with tag names. An additional null character marks the end of the sequence. Example: ``tag1\0tag2\0tag3\0\0``. To iterate over the tags you can use :c:func:`yr_rule_tags_foreach`. .. c:member:: YR_META* metas Pointer to a sequence of :c:type:`YR_META` structures. To iterate over the structures use :c:func:`yr_rule_metas_foreach`. .. c:member:: YR_STRING* strings Pointer to a sequence of :c:type:`YR_STRING` structures. To iterate over the structures use :c:func:`yr_rule_strings_foreach`. .. c:member:: YR_NAMESPACE* ns Pointer to a :c:type:`YR_NAMESPACE` structure. .. c:type:: YR_RULES Data structure representing a set of compiled rules. .. c:type:: YR_STREAM .. versionadded:: 3.4.0 Data structure representing a stream used with functions :c:func:`yr_rules_load_stream` and :c:func:`yr_rules_save_stream`. .. c:member:: void* user_data A user-defined pointer. .. c:member:: YR_STREAM_READ_FUNC read A pointer to the stream's read function provided by the user. .. c:member:: YR_STREAM_WRITE_FUNC write A pointer to the stream's write function provided by the user. .. c:type:: YR_STRING Data structure representing a string declared in a rule. .. c:member:: const char* identifier String identifier. .. c:type:: YR_NAMESPACE Data structure representing a rule namespace. .. c:member:: const char* name Rule namespace. Functions --------- .. c:function:: int yr_initialize(void) Initialize the library. Must be called by the main thread before using any other function. Return :c:macro:`ERROR_SUCCESS` on success another error code in case of error. The list of possible return codes vary according to the modules compiled into YARA. .. c:function:: int yr_finalize(void) Finalize the library. Must be called by the main free to release any resource allocated by the library. Return :c:macro:`ERROR_SUCCESS` on success another error code in case of error. The list of possible return codes vary according to the modules compiled into YARA. .. c:function:: int yr_compiler_create(YR_COMPILER** compiler) Create a YARA compiler. You must pass the address of a pointer to a :c:type:`YR_COMPILER`, the function will set the pointer to the newly allocated compiler. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` .. c:function:: void yr_compiler_destroy(YR_COMPILER* compiler) Destroy a YARA compiler. .. c:function:: void yr_compiler_set_callback(YR_COMPILER* compiler, YR_COMPILER_CALLBACK_FUNC callback, void* user_data) .. versionchanged:: 3.3.0 Set a callback for receiving error and warning information. The *user_data* pointer is passed to the callback function. .. c:function:: void yr_compiler_set_include_callback(YR_COMPILER* compiler, YR_COMPILER_INCLUDE_CALLBACK_FUNC callback, YR_COMPILER_INCLUDE_FREE_FUNC include_free, void* user_data) .. versionadded:: 3.7.0 Set a callback to provide rules from a custom source when ``include`` directive is invoked. The *user_data* pointer is untouched and passed back to the callback function and to the free function. Once the callback's result is no longer needed, the include_free function will be called. If the memory does not need to be freed, include_free can be set to NULL. If *callback* is set to ``NULL`` support for include directives is disabled. .. c:function:: int yr_compiler_add_file(YR_COMPILER* compiler, FILE* file, const char* namespace, const char* file_name) Compile rules from a *file*. Rules are put into the specified *namespace*, if *namespace* is ``NULL`` they will be put into the default namespace. *file_name* is the name of the file for error reporting purposes and can be set to ``NULL``. Returns the number of errors found during compilation. .. c:function:: int yr_compiler_add_fd(YR_COMPILER* compiler, YR_FILE_DESCRIPTOR rules_fd, const char* namespace, const char* file_name) .. versionadded:: 3.6.0 Compile rules from a *file descriptor*. Rules are put into the specified *namespace*, if *namespace* is ``NULL`` they will be put into the default namespace. *file_name* is the name of the file for error reporting purposes and can be set to ``NULL``. Returns the number of errors found during compilation. .. c:function:: int yr_compiler_add_string(YR_COMPILER* compiler, const char* string, const char* namespace_) Compile rules from a *string*. Rules are put into the specified *namespace*, if *namespace* is ``NULL`` they will be put into the default namespace. Returns the number of errors found during compilation. .. c:function:: int yr_compiler_get_rules(YR_COMPILER* compiler, YR_RULES** rules) Get the compiled rules from the compiler. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` .. c:function:: int yr_compiler_define_integer_variable(YR_COMPILER* compiler, const char* identifier, int64_t value) Define an integer external variable. .. c:function:: int yr_compiler_define_float_variable(YR_COMPILER* compiler, const char* identifier, double value) Define a float external variable. .. c:function:: int yr_compiler_define_boolean_variable(YR_COMPILER* compiler, const char* identifier, int value) Define a boolean external variable. .. c:function:: int yr_compiler_define_string_variable(YR_COMPILER* compiler, const char* identifier, const char* value) Define a string external variable. .. c:function:: int yr_rules_define_integer_variable(YR_RULES* rules, const char* identifier, int64_t value) Define an integer external variable. .. c:function:: int yr_rules_define_boolean_variable(YR_RULES* rules, const char* identifier, int value) Define a boolean external variable. .. c:function:: int yr_rules_define_float_variable(YR_RULES* rules, const char* identifier, double value) Define a float external variable. .. c:function:: int yr_rules_define_string_variable(YR_RULES* rules, const char* identifier, const char* value) Define a string external variable. .. c:function:: void yr_rules_destroy(YR_RULES* rules) Destroy compiled rules. .. c:function:: int yr_rules_save(YR_RULES* rules, const char* filename) Save compiled *rules* into the file specified by *filename*. Only rules obtained from :c:func:`yr_compiler_get_rules` can be saved. Those obtained from :c:func:`yr_rules_load` or :c:func:`yr_rules_load_stream` can not be saved. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_COULD_NOT_OPEN_FILE` .. c:function:: int yr_rules_save_stream(YR_RULES* rules, YR_STREAM* stream) .. versionadded:: 3.4.0 Save compiled *rules* into *stream*. Only rules obtained from :c:func:`yr_compiler_get_rules` can be saved. Those obtained from :c:func:`yr_rules_load` or :c:func:`yr_rules_load_stream` can not be saved. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` .. c:function:: int yr_rules_load(const char* filename, YR_RULES** rules) Load compiled rules from the file specified by *filename*. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_COULD_NOT_OPEN_FILE` :c:macro:`ERROR_INVALID_FILE` :c:macro:`ERROR_CORRUPT_FILE` :c:macro:`ERROR_UNSUPPORTED_FILE_VERSION` .. c:function:: int yr_rules_load_stream(YR_STREAM* stream, YR_RULES** rules) .. versionadded:: 3.4.0 Load compiled rules from *stream*. Rules loaded this way can not be saved back using :c:func:`yr_rules_save_stream`. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_INVALID_FILE` :c:macro:`ERROR_CORRUPT_FILE` :c:macro:`ERROR_UNSUPPORTED_FILE_VERSION` .. c:function:: int yr_rules_scan_mem(YR_RULES* rules, const uint8_t* buffer, size_t buffer_size, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) Scan a memory buffer. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` .. c:function:: int yr_rules_scan_file(YR_RULES* rules, const char* filename, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) Scan a file. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_COULD_NOT_MAP_FILE` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` .. c:function:: int yr_rules_scan_fd(YR_RULES* rules, YR_FILE_DESCRIPTOR fd, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) Scan a file descriptor. In POSIX systems ``YR_FILE_DESCRIPTOR`` is an ``int``, as returned by the `open()` function. In Windows ``YR_FILE_DESCRIPTOR`` is a ``HANDLE`` as returned by `CreateFile()`. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_COULD_NOT_MAP_FILE` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` .. c:function:: yr_rule_tags_foreach(rule, tag) Iterate over the tags of a given rule running the block of code that follows each time with a different value for *tag* of type ``const char*``. Example: .. code-block:: c const char* tag; /* rule is a YR_RULE object */ yr_rule_tags_foreach(rule, tag) { ..do something with tag } .. c:function:: yr_rule_metas_foreach(rule, meta) Iterate over the :c:type:`YR_META` structures associated with a given rule running the block of code that follows each time with a different value for *meta*. Example: .. code-block:: c YR_META* meta; /* rule is a YR_RULE object */ yr_rule_metas_foreach(rule, meta) { ..do something with meta } .. c:function:: yr_rule_strings_foreach(rule, string) Iterate over the :c:type:`YR_STRING` structures associated with a given rule running the block of code that follows each time with a different value for *string*. Example: .. code-block:: c YR_STRING* string; /* rule is a YR_RULE object */ yr_rule_strings_foreach(rule, string) { ..do something with string } .. c:function:: yr_string_matches_foreach(context, string, match) Iterate over the :c:type:`YR_MATCH` structures that represent the matches found for a given string during a scan running the block of code that follows, each time with a different value for *match*. The `context` argument is a pointer to a :c:type:`YR_SCAN_CONTEXT` that is passed to the callback function and `string` is a pointer to a :c:type:`YR_STRING`. Example: .. code-block:: c YR_MATCH* match; /* context is a YR_SCAN_CONTEXT* and string is a YR_STRING* */ yr_string_matches_foreach(context, string, match) { ..do something with match } .. c:function:: yr_rules_foreach(rules, rule) Iterate over each :c:type:`YR_RULE` in a :c:type:`YR_RULES` object running the block of code that follows each time with a different value for *rule*. Example: .. code-block:: c YR_RULE* rule; /* rules is a YR_RULES object */ yr_rules_foreach(rules, rule) { ..do something with rule } .. c:function:: void yr_rule_disable(YR_RULE* rule) .. versionadded:: 3.7.0 Disable the specified rule. Disabled rules are completely ignored during the scanning process and they won't match. If the disabled rule is used in the condition of some other rule the value for the disabled rule is neither true nor false but undefined. For more information about undefined values see :ref:`undefined-values`. .. c:function:: void yr_rule_enable(YR_RULE* rule) .. versionadded:: 3.7.0 Enables the specified rule. After being disabled with :c:func:`yr_rule_disable` a rule can be enabled again by using this function. .. c:function:: int yr_scanner_create(YR_RULES* rules, YR_SCANNER **scanner) .. versionadded:: 3.8.0 Creates a new scanner that can be used for scanning data with the provided provided rules. `scanner` must be a pointer to a :c:type:`YR_SCANNER`, the function will set the pointer to the newly allocated scanner. Returns one of the following error codes: :c:macro:`ERROR_INSUFFICIENT_MEMORY` .. c:function:: void yr_scanner_destroy(YR_SCANNER *scanner) .. versionadded:: 3.8.0 Destroy a scanner. After using a scanner it must be destroyed with this function. .. c:function:: void yr_scanner_set_callback(YR_SCANNER *scanner, YR_CALLBACK_FUNC callback, void* user_data) .. versionadded:: 3.8.0 Set a callback function that will be called for reporting any matches found by the scanner. .. c:function:: void yr_scanner_set_timeout(YR_SCANNER* scanner, int timeout) .. versionadded:: 3.8.0 Set the maximum number of seconds that the scanner will spend in any call to `yr_scanner_scan_xxx`. .. c:function:: void yr_scanner_set_flags(YR_SCANNER* scanner, int flags) .. versionadded:: 3.8.0 Set the flags that will be used by any call to `yr_scanner_scan_xxx`. The supported flags are: ``SCAN_FLAGS_FAST_MODE``: Enable fast scan mode. ``SCAN_FLAGS_NO_TRYCATCH``: Disable exception handling. ``SCAN_FLAGS_REPORT_RULES_MATCHING``: If this ``SCAN_FLAGS_REPORT_RULES_NOT_MATCHING`` .. c:function:: int yr_scanner_define_integer_variable(YR_SCANNER* scanner, const char* identifier, int64_t value) .. versionadded:: 3.8.0 Define an integer external variable. .. c:function:: int yr_scanner_define_boolean_variable(YR_SCANNER* scanner, const char* identifier, int value) .. versionadded:: 3.8.0 Define a boolean external variable. .. c:function:: int yr_scanner_define_float_variable(YR_SCANNER* scanner, const char* identifier, double value) .. versionadded:: 3.8.0 Define a float external variable. .. c:function:: int yr_scanner_define_string_variable(YR_SCANNER* scanner, const char* identifier, const char* value) .. versionadded:: 3.8.0 Define a string external variable. .. c:function:: int yr_scanner_scan_mem_blocks(YR_SCANNER* scanner, YR_MEMORY_BLOCK_ITERATOR* iterator) .. versionadded:: 3.8.0 Scan a series of memory blocks that are provided by a :c:type:`YR_MEMORY_BLOCK_ITERATOR`. The iterator has a pair of `first` and `next` functions that must return the first and next blocks respectively. When these functions return `NULL` it indicates that there are not more blocks to scan. In YARA 4.1 and later the `first` and `next` functions can return `NULL` and set the `last_error` field in :c:type:`YR_MEMORY_BLOCK_ITERATOR` to :c:macro:`ERROR_BLOCK_NOT_READY`. This indicates that the iterator is not able to return the next block yet, but the operation may be retried. In such cases `yr_scanner_scan_mem_blocks` also returns :c:macro:`ERROR_BLOCK_NOT_READY` but the scanner maintains its state and this function can be called again for continuing the scanning where it was left. This can be done multiple times until the block is ready and the iterator is able to return it. Notice however that once the iterator completes a full iteration, any subsequent iteration should proceed without returning :c:macro:`ERROR_BLOCK_NOT_READY`. During the first iteration the iterator should store in memory any information that it needs about the blocks, so that it can be iterated again without relying on costly operations that may result in a :c:macro:`ERROR_BLOCK_NOT_READY` error. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` :c:macro:`ERROR_BLOCK_NOT_READY` .. c:function:: int yr_scanner_scan_mem(YR_SCANNER* scanner, const uint8_t* buffer, size_t buffer_size) .. versionadded:: 3.8.0 Scan a memory buffer. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` .. c:function:: int yr_scanner_scan_file(YR_SCANNER* scanner, const char* filename) .. versionadded:: 3.8.0 Scan a file. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` .. c:function:: int yr_scanner_scan_fd(YR_SCANNER* scanner, YR_FILE_DESCRIPTOR fd) .. versionadded:: 3.8.0 Scan a file descriptor. In POSIX systems ``YR_FILE_DESCRIPTOR`` is an ``int``, as returned by the `open()` function. In Windows ``YR_FILE_DESCRIPTOR`` is a ``HANDLE`` as returned by `CreateFile()`. Returns one of the following error codes: :c:macro:`ERROR_SUCCESS` :c:macro:`ERROR_INSUFFICIENT_MEMORY` :c:macro:`ERROR_TOO_MANY_SCAN_THREADS` :c:macro:`ERROR_SCAN_TIMEOUT` :c:macro:`ERROR_CALLBACK_ERROR` :c:macro:`ERROR_TOO_MANY_MATCHES` .. c:function:: YR_RULE* yr_scanner_last_error_rule(YR_SCANNER* scanner) .. versionadded:: 3.8.0 Return a pointer to the ``YR_RULE`` which triggered a scanning error. In the case where the rule is unable to be determined, NULL is returned. .. c:function:: YR_RULE* yr_scanner_last_error_string(YR_SCANNER* scanner) .. versionadded:: 3.8.0 Return a pointer to the ``YR_STRING`` which triggered a scanning error. Error codes ----------- .. c:macro:: ERROR_SUCCESS Everything went fine. .. c:macro:: ERROR_INSUFFICIENT_MEMORY Insufficient memory to complete the operation. .. c:macro:: ERROR_COULD_NOT_OPEN_FILE File could not be opened. .. c:macro:: ERROR_COULD_NOT_MAP_FILE File could not be mapped into memory. .. c:macro:: ERROR_INVALID_FILE File is not a valid rules file. .. c:macro:: ERROR_CORRUPT_FILE Rules file is corrupt. .. c:macro:: ERROR_UNSUPPORTED_FILE_VERSION File was generated by a different YARA and can't be loaded by this version. .. c:macro:: ERROR_TOO_MANY_SCAN_THREADS Too many threads trying to use the same :c:type:`YR_RULES` object simultaneously. The limit is defined by ``YR_MAX_THREADS`` in *./include/yara/limits.h* .. c:macro:: ERROR_SCAN_TIMEOUT Scan timed out. .. c:macro:: ERROR_CALLBACK_ERROR Callback returned an error. .. c:macro:: ERROR_TOO_MANY_MATCHES Too many matches for some string in your rules. This usually happens when your rules contains very short or very common strings like ``01 02`` or ``FF FF FF FF``. The limit is defined by ``YR_MAX_STRING_MATCHES`` in *./include/yara/limits.h* .. c:macro:: ERROR_BLOCK_NOT_READY Next memory block to scan is not ready; custom iterators may return this. yara-4.1.3/docs/commandline.rst000066400000000000000000000114011413423160300164150ustar00rootroot00000000000000.. _command-line: ********************************** Running YARA from the command-line ********************************** In order to invoke YARA you’ll need two things: a file with the rules you want to use and the target to be scanned. The target can be a file, a folder, or a process. :: yara [OPTIONS] RULES_FILE TARGET In YARA 3.8 and below ``RULES_FILE`` was allowed to be a file with rules in source form or in compiled form indistinctly. In YARA 3.9 you need to explictly specify that ``RULES_FILE`` contains compiled rules by using the -C flag. :: yara [OPTIONS] -C RULES_FILE TARGET This is a security measure to prevent users from inadvertenly using compiled rules coming from a third-party. Using compiled rules from untrusted sources can lead to the execution of malicious code in your computer. For compiling rules beforhand you can use the ``yarac`` tool. This way can save time, because for YARA it is faster to load compiled rules than compiling the same rules over and over again. You can also pass multiple source files to `yara` like in the following example:: yara [OPTIONS] RULES_FILE_1 RULES_FILE_2 RULES_FILE_3 TARGET Notice however that this only works for rules in source form. When invoking YARA with compiled rules a single file is accepted. In the example above all rules share the same "default" namespace, which means that rule identifiers must be unique among all files. However you can specify a namespace for individual files. For example :: yara [OPTIONS] namespace1:RULES_FILE_1 RULES_FILE_2 RULES_FILE_3 TARGET In this case ``RULE_FILE_1`` uses ``namespace1`` while ``RULES_FILE_2`` and ``RULES_FILE_3`` share the default namespace. In all cases rules will be applied to the target specified as the last argument to YARA, if it’s a path to a directory all the files contained in it will be scanned. By default YARA does not attempt to scan directories recursively, but you can use the ``-r`` option for that. Available options are: .. program:: yara .. option:: -t --tag= Print rules tagged as and ignore the rest. .. option:: -i --identifier= Print rules named and ignore the rest. .. option:: -C --compiled-rules RULES_FILE contains rules already compiled with yarac. .. option:: -c --count Print only number of matches. .. option:: -n Print not satisfied rules only (negate). .. option:: -D --print-module-data Print module data. .. option:: -g --print-tags Print tags. .. option:: -m --print-meta Print metadata. .. option:: -s --print-strings Print matching strings. .. option:: -L --print-string-length Print length of matching strings. .. option:: -e --print-namespace Print rules' namespace. .. option:: -p --threads= Use the specified of threads to scan a directory. .. option:: -l --max-rules= Abort scanning after matching a number of rules. .. option:: -a --timeout= Abort scanning after a number of seconds has elapsed. .. option:: -k --stack-size= Allocate a stack size of "slots" number of slots. Default: 16384. This will allow you to use larger rules, albeit with more memory overhead. .. versionadded:: 3.5.0 .. option:: --max-strings-per-rule= Set maximum number of strings per rule (default=10000). If a rule has more then the specified number of strings an error will occur. .. versionadded:: 3.7.0 .. option:: -d = Define external variable. .. option:: -x = Pass file's content as extra data to module. .. option:: --scan-list Scan files listed in FILE, one per line. .. option:: -r --recursive Recursively search for directories. It follows symlinks. .. option:: -f --fast-scan Fast matching mode. .. option:: -w --no-warnings Disable warnings. .. option:: --fail-on-warnings Treat warnings as errors. Has no effect if used with --no-warnings. .. option:: -v --version Show version information. .. option:: -h --help Show help. Here you have some examples: * Apply rule in */foo/bar/rules* to all files in the current directory. Subdirectories are not scanned:: yara /foo/bar/rules . * Apply rules in */foo/bar/rules* to *bazfile*. Only reports rules tagged as *Packer* or *Compiler*:: yara -t Packer -t Compiler /foo/bar/rules bazfile * Scan all files in the */foo* directory and its subdirectories:: yara /foo/bar/rules -r /foo * Defines three external variables *mybool*, *myint* and *mystring*:: yara -d mybool=true -d myint=5 -d mystring="my string" /foo/bar/rules bazfile * Apply rules in */foo/bar/rules* to *bazfile* while passing the content of *cuckoo_json_report* to the cuckoo module:: yara -x cuckoo=cuckoo_json_report /foo/bar/rules bazfile yara-4.1.3/docs/conf.py000066400000000000000000000201111413423160300146720ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # yara documentation build configuration file, created by # sphinx-quickstart on Tue Jul 8 11:04:03 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'yara' copyright = u'2014-2021, VirusTotal' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '4.1' # The full version, including alpha/beta/rc tags. release = '4.1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. try: import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except: html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'yaradoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'yara.tex', u'yara Documentation', u'Victor M. Alvarez', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'yara', u'yara Documentation', [u'Victor M. Alvarez'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'yara', u'yara Documentation', u'Victor M. Alvarez', 'yara', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False yara-4.1.3/docs/docutils.conf000066400000000000000000000000361413423160300160740ustar00rootroot00000000000000[parsers] smart_quotes: false yara-4.1.3/docs/gettingstarted.rst000066400000000000000000000134311413423160300171640ustar00rootroot00000000000000*************** Getting started *************** YARA is a multi-platform program running on Windows, Linux and Mac OS X. You can find the latest release at https://github.com/VirusTotal/yara/releases. .. _compiling-yara: Compiling and installing YARA ============================= Download the source tarball and get prepared for compiling it:: tar -zxf yara-4.1.0.tar.gz cd yara-4.1.0 ./bootstrap.sh Make sure you have ``automake``, ``libtool``, ``make`` and ``gcc`` and ``pkg-config`` installed in your system. Ubuntu and Debian users can use:: sudo apt-get install automake libtool make gcc pkg-config If you plan to modify YARA's source code you may also need ``flex`` and ``bison`` for generating lexers and parsers:: sudo apt-get install flex bison Compile and install YARA in the standard way:: ./bootstrap.sh ./configure make sudo make install Run the test cases to make sure that everything is fine:: make check Some of YARA's features depend on the OpenSSL library. Those features are enabled only if you have the OpenSSL library installed in your system. If not, YARA is going to work fine but you won't be able to use the disabled features. The ``configure`` script will automatically detect if OpenSSL is installed or not. If you want to enforce the OpenSSL-dependent features you must pass ``--with-crypto`` to the ``configure`` script. Ubuntu and Debian users can use ``sudo apt-get install libssl-dev`` to install the OpenSSL library. The following modules are not compiled into YARA by default: * cuckoo * magic * dotnet If you plan to use them you must pass the corresponding ``--enable-`` arguments to the ``configure`` script. For example:: ./configure --enable-cuckoo ./configure --enable-magic ./configure --enable-dotnet ./configure --enable-cuckoo --enable-magic --enable-dotnet Modules usually depend on external libraries, depending on the modules you choose to install you'll need the following libraries: * cuckoo: Depends on `Jansson `_ for parsing JSON. Some Ubuntu and Debian versions already include a package named ``libjansson-dev``, if ``sudo apt-get install libjansson-dev`` doesn't work for you then get the source code from `its repository `_. * magic: Depends on *libmagic*, a library used by the Unix standard program `file `_. Ubuntu, Debian and CentOS include a package ``libmagic-dev``. The source code can be found `here `_. Installing with vcpkg --------------------- You can also download and install YARA using the `vcpkg `_ dependency manager:: git clone https://github.com/microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install vcpkg install yara The YARA port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please `create an issue or pull request `_ on the vcpkg repository. Installing on Windows --------------------- Compiled binaries for Windows in both 32 and 64 bit flavors can be found in the link below. Just download the version you want, unzip the archive, and put the ``yara.exe`` and ``yarac.exe`` binaries anywhere in your disk. `Download Windows binaries `_ To install YARA using `Scoop `_ or `Chocolatey `_, simply type ``scoop install yara`` or ``choco install yara``. The integration with both `Scoop` and `Chocolatey` are not maintained their respective teams, not by the YARA authors. Installing on Mac OS X with Homebrew ------------------------------------ To install YARA using `Homebrew `_, simply type ``brew install yara``. Installing ``yara-python`` ---------------------- If you plan to use YARA from your Python scripts you need to install the ``yara-python`` extension. Please refer to https://github.com/VirusTotal/yara-python for instructions on how to install it. Running YARA for the first time =============================== Now that you have installed YARA you can write a very simple rule and use the command-line tool to scan some file: .. code-block:: sh echo rule dummy { condition: true } > my_first_rule yara my_first_rule my_first_rule Don't get confused by the repeated ``my_first_rule`` in the arguments to ``yara``, I'm just passing the same file as both the rules and the file to be scanned. You can pass any file you want to be scanned (second argument). If everything goes fine you should get the following output:: dummy my_first_rule Which means that the file ``my_first_rule`` is matching the rule named ``dummy``. If you get an error like this:: yara: error while loading shared libraries: libyara.so.2: cannot open shared object file: No such file or directory It means that the loader is not finding the ``libyara`` library which is located in ``/usr/local/lib``. In some Linux flavors the loader doesn't look for libraries in this path by default, we must instruct it to do so by adding ``/usr/local/lib`` to the loader configuration file ``/etc/ld.so.conf``:: sudo sh -c 'echo "/usr/local/lib" >> /etc/ld.so.conf' sudo ldconfig If you're using Windows PowerShell as your command shell, ``yara my_first_rule my_first_rule`` may return this error:: my_first_rule(1): error: non-ascii character You can avoid this by using the ``Set-Content`` cmdlet to specify ascii output when creating your rule file:: Set-Content -path .\my_first_rule -Value "rule dummy { condition: true }" -Encoding Ascii .\yara my_first_rule my_first_rule yara-4.1.3/docs/index.rst000066400000000000000000000030501413423160300152370ustar00rootroot00000000000000.. yara documentation master file, created by sphinx-quickstart on Tue Jul 8 11:04:03 2014. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to YARA's documentation! ================================ YARA is a tool aimed at (but not limited to) helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families (or whatever you want to describe) based on textual or binary patterns. Each description, a.k.a. rule, consists of a set of strings and a boolean expression which determine its logic. Let's see an example: .. code-block:: yara rule silent_banker : banker { meta: description = "This is just an example" threat_level = 3 in_the_wild = true strings: $a = {6A 40 68 00 30 00 00 6A 14 8D 91} $b = {8D 4D B0 2B C1 83 C0 27 99 6A 4E 59 F7 F9} $c = "UVODFRYSIHLNWPEJXQZAKCBGMT" condition: $a or $b or $c } The above rule is telling YARA that any file containing one of the three strings must be reported as silent_banker. This is just a simple example, more complex and powerful rules can be created by using wild-cards, case-insensitive strings, regular expressions, special operators and many other features that you'll find explained in this documentation. Contents: .. toctree:: :maxdepth: 3 gettingstarted writingrules modules writingmodules commandline yarapython capi yara-4.1.3/docs/make.bat000066400000000000000000000150511413423160300150070ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\yara.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\yara.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end yara-4.1.3/docs/modules.rst000066400000000000000000000011321413423160300155770ustar00rootroot00000000000000******* Modules ******* Modules are the method YARA provides for extending its features. They allow you to define data structures and functions which can be used in your rules to express more complex conditions. Here you'll find described some modules officially distributed with YARA, but you can also learn how to write your own modules in the :ref:`writing-modules` section. .. toctree:: :maxdepth: 3 PE ELF Cuckoo Magic Hash Math Dotnet Time yara-4.1.3/docs/modules/000077500000000000000000000000001413423160300150505ustar00rootroot00000000000000yara-4.1.3/docs/modules/cuckoo.rst000066400000000000000000000123431413423160300170700ustar00rootroot00000000000000.. _cuckoo-module: ############# Cuckoo module ############# The Cuckoo module enables you to create YARA rules based on behavioral information generated by `Cuckoo sandbox `_. While scanning a PE file with YARA, you can pass additional information about its behavior to the ``cuckoo`` module and create rules based not only on what it *contains*, but also on what it *does*. .. important:: This module is not built into YARA by default, to learn how to include it refer to :ref:`compiling-yara`. Good news for Windows users: this module is already included in the official Windows binaries. Suppose that you're interested in executable files sending a HTTP request to http://someone.doingevil.com. In previous versions of YARA you had to settle with: .. code-block:: yara rule evil_doer { strings: $evil_domain = "http://someone.doingevil.com" condition: $evil_domain } The problem with this rule is that the domain name could be contained in the file for perfectly valid reasons not related with sending HTTP requests to http://someone.doingevil.com. Furthermore, the malicious executable could contain the domain name ciphered or obfuscated, in which case your rule would be completely useless. But now with the ``cuckoo`` module you can take the behavior report generated for the executable file by your Cuckoo sandbox, pass it alongside the executable file to YARA, and write a rule like this: .. code-block:: yara import "cuckoo" rule evil_doer { condition: cuckoo.network.http_request(/http:\/\/someone\.doingevil\.com/) } Of course you can mix your behavior-related conditions with good old string-based conditions: .. code-block:: yara import "cuckoo" rule evil_doer { strings: $some_string = { 01 02 03 04 05 06 } condition: $some_string and cuckoo.network.http_request(/http:\/\/someone\.doingevil\.com/) } But how do we pass the behavior information to the ``cuckoo`` module? Well, in the case of the command-line tool you must use the ``-x`` option in this way:: $yara -x cuckoo=behavior_report_file rules_file pe_file ``behavior_report_file`` is the path to a file containing the behavior file generated by the Cuckoo sandbox in JSON format. If you are using ``yara-python`` then you must pass the behavior report in the ``modules_data`` argument for the ``match`` method: .. code-block:: python import yara rules = yara.compile('./rules_file') report_file = open('./behavior_report_file') report_data = report_file.read() rules.match(pe_file, modules_data={'cuckoo': bytes(report_data)}) Reference --------- .. default-domain:: c .. type:: network .. function:: http_request(regexp) Function returning true if the program sent a HTTP request to a URL matching the provided regular expression. *Example: cuckoo.network.http_request(/evil\\.com/)* .. function:: http_get(regexp) Similar to :func:`http_request`, but only takes into account GET requests. .. function:: http_post(regexp) Similar to :func:`http_request`, but only takes into account POST requests. .. function:: http_user_agent(regexp) Function returning true if the program sent a HTTP request with a user agent matching the provided regular expression. *Example: cuckoo.network.http_user_agent(/MSIE 6\\.0/)* .. function:: dns_lookup(regexp) Function returning true if the program sent a domain name resolution request for a domain matching the provided regular expression. *Example: cuckoo.network.dns_lookup(/evil\\.com/)* .. function:: host(regexp) Function returning true if the program contacted an IP address matching the provided regular expression. *Example: cuckoo.network.host(/192\\.168\\.1\\.1/)* .. function:: tcp(regexp, port) Function returning true if the program contacted an IP address matching the provided regular expression, over TCP on the provided port number. *Example: cuckoo.network.tcp(/192\\.168\\.1\\.1/, 443)* .. function:: udp(regexp, port) Function returning true if the program contacted an IP address matching the provided regular expression, over UDP on the provided port number. *Example: cuckoo.network.udp(/192\\.168\\.1\\.1/, 53)* .. type:: registry .. function:: key_access(regexp) Function returning true if the program accessed a registry entry matching the provided regular expression. *Example: cuckoo.registry.key_access(/\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Run/)* .. type:: filesystem .. function:: file_access(regexp) Function returning true if the program accessed a file matching the provided regular expression. *Example: cuckoo.filesystem.file_access(/autoexec\\.bat/)* .. type:: sync .. function:: mutex(regexp) Function returning true if the program opens or creates a mutex matching the provided regular expression. *Example: cuckoo.sync.mutex(/EvilMutexName/)* yara-4.1.3/docs/modules/dotnet.rst000066400000000000000000000102751413423160300171040ustar00rootroot00000000000000 .. _dotnet-module: ############# dotnet module ############# .. versionadded:: 3.6.0 The dotnet module allows you to create more fine-grained rules for .NET files by using attributes and features of the .NET file format. Let's see some examples: .. code-block:: yara import "dotnet" rule not_exactly_five_streams { condition: dotnet.number_of_streams != 5 } rule blop_stream { condition: for any i in (0..dotnet.number_of_streams - 1): (dotnet.streams[i].name == "#Blop") } Reference --------- .. c:type:: version The version string contained in the metadata root. *Example: dotnet.version == "v2.0.50727"* .. c:type:: module_name The name of the module. *Example: dotnet.module_name == "axs"* .. c:type:: number_of_streams The number of streams in the file. .. c:type:: streams A zero-based array of stream objects, one for each stream contained in the file. Individual streams can be accessed by using the [] operator. Each stream object has the following attributes: .. c:member:: name Stream name. .. c:member:: offset Stream offset. .. c:member:: size Stream size. *Example: dotnet.streams[0].name == "#~"* .. c:type:: number_of_guids The number of GUIDs in the guids array. .. c:type:: guids A zero-based array of strings, one for each GUID. Individual guids can be accessed by using the [] operator. *Example: dotnet.guids[0] == "99c08ffd-f378-a891-10ab-c02fe11be6ef"* .. c:type:: number_of_resources The number of resources in the .NET file. These are different from normal PE resources. .. c:type:: resources A zero-based array of resource objects, one for each resource the .NET file has. Individual resources can be accessed by using the [] operator. Each resource object has the following attributes: .. c:member:: offset Offset for the resource data. .. c:member:: length Length of the resource data. .. c:member:: name Name of the resource (string). *Example: uint16be(dotnet.resources[0].offset) == 0x4d5a* .. c:type:: assembly Object for .NET assembly information. .. c:member:: version An object with integer values representing version information for this assembly. Attributes are: ``major`` ``minor`` ``build_number`` ``revision_number`` .. c:member:: name String containing the assembly name. .. c:member:: culture String containing the culture (language/country/region) for this assembly. *Example: dotnet.assembly.name == "Keylogger"* *Example: dotnet.assembly.version.major == 7 and dotnet.assembly.version.minor == 0* .. c:type:: number_of_modulerefs The number of module references in the .NET file. .. c:type:: modulerefs A zero-based array of strings, one for each module reference the .NET file has. Individual module references can be accessed by using the [] operator. *Example: dotnet.modulerefs[0] == "kernel32"* .. c:type:: typelib The typelib of the file. .. c:type:: assembly_refs Object for .NET assembly reference information. .. c:member:: version An object with integer values representing version information for this assembly. Attributes are: ``major`` ``minor`` ``build_number`` ``revision_number`` .. c:member:: name String containing the assembly name. .. c:member:: public_key_or_token String containing the public key or token which identifies the author of this assembly. .. c:type:: number_of_user_strings The number of user strings in the file. .. c:type:: user_strings An zero-based array of user strings, one for each stream contained in the file. Individual strings can be accessed by using the [] operator. .. c:type:: number_of_field_offsets The number of fields in the field_offsets array. .. c:type:: field_offsets A zero-based array of integers, one for each field. Individual field offsets can be accessed by using the [] operator. *Example: dotnet.field_offsets[0] == 8675309* yara-4.1.3/docs/modules/elf.rst000066400000000000000000000215621413423160300163560ustar00rootroot00000000000000 .. _elf-module: ########## ELF module ########## .. versionadded:: 3.2.0 The ELF module is very similar to the :ref:`pe-module`, but for ELF files. This module exposes most of the fields present in an ELF header. Let's see some examples: .. code-block:: yara import "elf" rule single_section { condition: elf.number_of_sections == 1 } rule elf_64 { condition: elf.machine == elf.EM_X86_64 } Reference --------- .. c:type:: type Integer with one of the following values: .. c:type:: ET_NONE No file type. .. c:type:: ET_REL Relocatable file. .. c:type:: ET_EXEC Executable file. .. c:type:: ET_DYN Shared object file. .. c:type:: ET_CORE Core file. *Example: elf.type == elf.ET_EXEC* .. c:type:: machine Integer with one of the following values: .. c:type:: EM_NONE .. c:type:: EM_M32 .. c:type:: EM_SPARC .. c:type:: EM_386 .. c:type:: EM_68K .. c:type:: EM_88K .. c:type:: EM_860 .. c:type:: EM_MIPS .. c:type:: EM_MIPS_RS3_LE .. c:type:: EM_PPC .. c:type:: EM_PPC64 .. c:type:: EM_ARM .. c:type:: EM_X86_64 .. c:type:: EM_AARCH64 *Example: elf.machine == elf.EM_X86_64* .. c:type:: entry_point Entry point raw offset or virtual address depending on whether YARA is scanning a file or process memory respectively. This is equivalent to the deprecated ``entrypoint`` keyword. .. c:type:: number_of_sections Number of sections in the ELF file. .. c:type:: sections A zero-based array of section objects, one for each section the ELF has. Individual sections can be accessed by using the [] operator. Each section object has the following attributes: .. c:member:: name Section's name. *Example: elf.sections[3].name == ".bss"* .. c:member:: size Section's size in bytes. Unless the section type is SHT_NOBITS, the section occupies sh_size bytes in the file. A section of :c:type:`SHT_NOBITS` may have a non-zero size, but it occupies no space in the file. .. c:member:: offset Offset from the beginning of the file to the first byte in the section. One section type, :c:type:`SHT_NOBITS` described below, occupies no space in the file, and its :c:member:`offset` member locates the conceptual placement in the file. .. c:member:: type Integer with one of the following values: .. c:type:: SHT_NULL This value marks the section as inactive; it does not have an associated section. Other members of the section header have undefined values. .. c:type:: SHT_PROGBITS The section holds information defined by the program, whose format and meaning are determined solely by the program. .. c:type:: SHT_SYMTAB The section holds a symbol table. .. c:type:: SHT_STRTAB The section holds a string table. An object file may have multiple string table sections. .. c:type:: SHT_RELA The section holds relocation entries. .. c:type:: SHT_HASH The section holds a symbol hash table. .. c:type:: SHT_DYNAMIC The section holds information for dynamic linking. .. c:type:: SHT_NOTE The section holds information that marks the file in some way. .. c:type:: SHT_NOBITS A section of this type occupies no space in the file but otherwise resembles :c:type:`SHT_PROGBITS`. .. c:type:: SHT_REL The section holds relocation entries. .. c:type:: SHT_SHLIB This section type is reserved but has unspecified semantics. .. c:type:: SHT_DYNSYM This section holds dynamic linking symbols. .. c:member:: flags Integer with section's flags as defined below: .. c:type:: SHF_WRITE The section contains data that should be writable during process execution. .. c:type:: SHF_ALLOC The section occupies memory during process execution. Some control sections do not reside in the memory image of an object file; this attribute is off for those sections. .. c:type:: SHF_EXECINSTR The section contains executable machine instructions. *Example: elf.sections[2].flags & elf.SHF_WRITE* .. c:member:: address .. versionadded:: 3.6.0 The virtual address the section starts at. .. c:type:: number_of_segments .. versionadded:: 3.4.0 Number of segments in the ELF file. .. c:type:: segments .. versionadded:: 3.4.0 A zero-based array of segment objects, one for each segment the ELF has. Individual segments can be accessed by using the [] operator. Each segment object has the following attributes: .. c:member:: alignment Value to which the segments are aligned in memory and in the file. .. c:member:: file_size Number of bytes in the file image of the segment. It may be zero. .. c:member:: flags A combination of the following segment flags: .. c:type:: PF_R The segment is readable. .. c:type:: PF_W The segment is writable. .. c:type:: PF_X The segment is executable. .. c:member:: memory_size In-memory segment size. .. c:member:: offset Offset from the beginning of the file where the segment resides. .. c:member:: physical_address On systems for which physical addressing is relevant, contains the segment's physical address. .. c:member:: type Type of segment indicated by one of the following values: .. c:type:: PT_NULL .. c:type:: PT_LOAD .. c:type:: PT_DYNAMIC .. c:type:: PT_INTERP .. c:type:: PT_NOTE .. c:type:: PT_SHLIB .. c:type:: PT_PHDR .. c:type:: PT_LOPROC .. c:type:: PT_HIPROC .. c:type:: PT_GNU_STACK .. c:member:: virtual_address Virtual address at which the segment resides in memory. .. c:type:: dynamic_section_entries .. versionadded:: 3.6.0 Number of entries in the dynamic section in the ELF file. .. c:type:: dynamic .. versionadded:: 3.6.0 A zero-based array of dynamic objects, one for each entry in found in the ELF's dynamic section. Individual dynamic objects can be accessed by using the [] operator. Each dynamic object has the following attributes: .. c:member:: type Value that describes the type of dynamic section. Builtin values are: .. c:type:: DT_NULL .. c:type:: DT_NEEDED .. c:type:: DT_PLTRELSZ .. c:type:: DT_PLTGOT .. c:type:: DT_HASH .. c:type:: DT_STRTAB .. c:type:: DT_SYMTAB .. c:type:: DT_RELA .. c:type:: DT_RELASZ .. c:type:: DT_RELAENT .. c:type:: DT_STRSZ .. c:type:: DT_SYMENT .. c:type:: DT_INIT .. c:type:: DT_FINI .. c:type:: DT_SONAME .. c:type:: DT_RPATH .. c:type:: DT_SYMBOLIC .. c:type:: DT_REL .. c:type:: DT_RELSZ .. c:type:: DT_RELENT .. c:type:: DT_PLTREL .. c:type:: DT_DEBUG .. c:type:: DT_TEXTREL .. c:type:: DT_JMPREL .. c:type:: DT_BIND_NOW .. c:type:: DT_INIT_ARRAY .. c:type:: DT_FINI_ARRAY .. c:type:: DT_INIT_ARRAYSZ .. c:type:: DT_FINI_ARRAYSZ .. c:type:: DT_RUNPATH .. c:type:: DT_FLAGS .. c:type:: DT_ENCODING .. c:member:: value A value associated with the given type. The type of value (address, size, etc.) is dependant on the type of dynamic entry. .. c:type:: symtab_entries .. versionadded:: 3.6.0 Number of entries in the symbol table found in the ELF file. .. c:type:: symtab .. versionadded:: 3.6.0 A zero-based array of symbol objects, one for each entry in found in the ELF's SYMBTAB. Individual symbol objects can be accessed by using the [] operator. Each symbol object has the following attributes: .. c:member:: name The symbol's name. .. c:member:: value A value associated with the symbol. Generally a virtual address. .. c:member:: size The symbol's size. .. c:member:: type The type of symbol. Built values are: .. c:type:: STT_NOTYPE .. c:type:: STT_OBJECT .. c:type:: STT_FUNC .. c:type:: STT_SECTION .. c:type:: STT_FILE .. c:type:: STT_COMMON .. c:type:: STT_TLS .. c:member:: bind The binding of the symbol. Builtin values are: .. c:type:: STB_LOCAL .. c:type:: STB_GLOBAL .. c:type:: STB_WEAK .. c:member:: shndx The section index which the symbol is associated with. yara-4.1.3/docs/modules/hash.rst000066400000000000000000000050541413423160300165310ustar00rootroot00000000000000 .. _hash-module: ########### Hash module ########### .. versionadded:: 3.2.0 The Hash module allows you to calculate hashes (MD5, SHA1, SHA256) from portions of your file and create signatures based on those hashes. .. important:: This module depends on the OpenSSL library. Please refer to :ref:`compiling-yara` for information about how to build OpenSSL-dependant features into YARA. Good news for Windows users: this module is already included in the official Windows binaries. .. warning:: The returned hash string is always in lowercase. This means that rule condition matching on hashes ``hash.md5(0, filesize) == "feba6c919e3797e7778e8f2e85fa033d"`` requires the hash string to be given in lowercase, otherwise the match condition will not work. (see https://github.com/VirusTotal/yara/issues/1004) .. c:function:: md5(offset, size) Returns the MD5 hash for *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned string is always in lowercase. *Example: hash.md5(0, filesize) == "feba6c919e3797e7778e8f2e85fa033d"* .. c:function:: md5(string) Returns the MD5 hash for the given string. *Example: hash.md5("dummy") == "275876e34cf609db118f3d84b799a790"* .. c:function:: sha1(offset, size) Returns the SHA1 hash for the *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned string is always in lowercase. .. c:function:: sha1(string) Returns the SHA1 hash for the given string. .. c:function:: sha256(offset, size) Returns the SHA256 hash for the *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned string is always in lowercase. .. c:function:: sha256(string) Returns the SHA256 hash for the given string. .. c:function:: checksum32(offset, size) Returns a 32-bit checksum for the *size* bytes starting at *offset*. The checksum is just the sum of all the bytes (unsigned). .. c:function:: checksum32(string) Returns a 32-bit checksum for the given string. The checksum is just the sum of all the bytes in the string (unsigned). .. c:function:: crc32(offset, size) Returns a crc32 checksum for the *size* bytes starting at *offset*. .. c:function:: crc32(string) Returns a crc32 checksum for the given string. yara-4.1.3/docs/modules/magic.rst000066400000000000000000000033621413423160300166660ustar00rootroot00000000000000 .. _magic-module: ############ Magic module ############ .. versionadded:: 3.1.0 The Magic module allows you to identify the type of the file based on the output of `file `_, the standard Unix command. .. important:: This module is not built into YARA by default, to learn how to include it refer to :ref:`compiling-yara`. Bad news for Windows users: **this module is not supported on Windows**. There are two functions in this module: :c:func:`type` and :c:func:`mime_type`. The first one returns the descriptive string returned by *file*, for example, if you run *file* against some PDF document you'll get something like this:: $file some.pdf some.pdf: PDF document, version 1.5 The :c:func:`type` function would return *"PDF document, version 1.5"* in this case. Using the :c:func:`mime_type` function is similar to passing the ``--mime`` argument to *file*.:: $file --mime some.pdf some.pdf: application/pdf; charset=binary :c:func:`mime_type` would return *"application/pdf"*, without the charset part. By experimenting a little with the *file* command you can learn which output to expect for different file types. These are a few examples: * JPEG image data, JFIF standard 1.01 * PE32 executable for MS Windows (GUI) Intel 80386 32-bit * PNG image data, 1240 x 1753, 8-bit/color RGBA, non-interlaced * ASCII text, with no line terminators * Zip archive data, at least v2.0 to extract .. c:function:: type() Function returning a string with the type of the file. *Example: magic.type() contains "PDF"* .. c:function:: mime_type() Function returning a string with the MIME type of the file. *Example: magic.mime_type() == "application/pdf"* yara-4.1.3/docs/modules/math.rst000066400000000000000000000074311413423160300165400ustar00rootroot00000000000000 .. _math-module: ########### Math module ########### .. versionadded:: 3.3.0 The Math module allows you to calculate certain values from portions of your file and create signatures based on those results. .. important:: Where noted these functions return floating point numbers. YARA is able to convert integers to floating point numbers during most operations. For example this will convert 7 to 7.0 automatically, because the return type of the entropy function is a floating point value: *math.entropy(0, filesize) >= 7* The one exception to this is when a function requires a floating point number as an argument. For example, this will cause a syntax error because the arguments must be floating point numbers: *math.in_range(2, 1, 3)* .. c:function:: entropy(offset, size) Returns the entropy for *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned value is a float. *Example: math.entropy(0, filesize) >= 7* .. c:function:: entropy(string) Returns the entropy for the given string. *Example: math.entropy("dummy") > 7* .. c:function:: monte_carlo_pi(offset, size) Returns the percentage away from Pi for the *size* bytes starting at *offset* when run through the Monte Carlo from Pi test. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned value is a float. *Example: math.monte_carlo_pi(0, filesize) < 0.07* .. c:function:: monte_carlo_pi(string) Return the percentage away from Pi for the given string. .. c:function:: serial_correlation(offset, size) Returns the serial correlation for the *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned value is a float between 0.0 and 1.0. *Example: math.serial_correlation(0, filesize) < 0.2* .. c:function:: serial_correlation(string) Return the serial correlation for the given string. .. c:function:: mean(offset, size) Returns the mean for the *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned value is a float. *Example: math.mean(0, filesize) < 72.0* .. c:function:: mean(string) Return the mean for the given string. .. c:function:: deviation(offset, size, mean) Returns the deviation from the mean for the *size* bytes starting at *offset*. When scanning a running process the *offset* argument should be a virtual address within the process address space. The returned value is a float. The mean of an equally distributed random sample of bytes is 127.5, which is available as the constant math.MEAN_BYTES. *Example: math.deviation(0, filesize, math.MEAN_BYTES) == 64.0* .. c:function:: deviation(string, mean) Return the deviation from the mean for the given string. .. c:function:: in_range(test, lower, upper) Returns true if the *test* value is between *lower* and *upper* values. The comparisons are inclusive. *Example: math.in_range(math.deviation(0, filesize, math.MEAN_BYTES), 63.9, 64,1)* .. c:function:: max(int, int) .. versionadded:: 3.8.0 Returns the maximum of two unsigned integer values. .. c:function:: min(int, int) .. versionadded:: 3.8.0 Returns the minimum of two unsigned integer values. .. c:function:: to_number(bool) .. versionadded:: 4.1.0 Returns 0 or 1, it's useful when writing a score based rule. *Example: math.to_number(SubRule1) \* 60 + math.to_number(SubRule2) \* 20 + math.to_number(SubRule3) \* 70 > 80* yara-4.1.3/docs/modules/pe.rst000066400000000000000000000711531413423160300162150ustar00rootroot00000000000000 .. _pe-module: ######### PE module ######### The PE module allows you to create more fine-grained rules for PE files by using attributes and features of the PE file format. This module exposes most of the fields present in a PE header and provides functions which can be used to write more expressive and targeted rules. Let's see some examples: .. code-block:: yara import "pe" rule single_section { condition: pe.number_of_sections == 1 } rule control_panel_applet { condition: pe.exports("CPlApplet") } rule is_dll { condition: pe.characteristics & pe.DLL } rule is_pe { condition: pe.is_pe } Reference --------- .. c:type:: machine .. versionchanged:: 3.3.0 Integer with one of the following values: .. c:type:: MACHINE_UNKNOWN .. c:type:: MACHINE_AM33 .. c:type:: MACHINE_AMD64 .. c:type:: MACHINE_ARM .. c:type:: MACHINE_ARMNT .. c:type:: MACHINE_ARM64 .. c:type:: MACHINE_EBC .. c:type:: MACHINE_I386 .. c:type:: MACHINE_IA64 .. c:type:: MACHINE_M32R .. c:type:: MACHINE_MIPS16 .. c:type:: MACHINE_MIPSFPU .. c:type:: MACHINE_MIPSFPU16 .. c:type:: MACHINE_POWERPC .. c:type:: MACHINE_POWERPCFP .. c:type:: MACHINE_R4000 .. c:type:: MACHINE_SH3 .. c:type:: MACHINE_SH3DSP .. c:type:: MACHINE_SH4 .. c:type:: MACHINE_SH5 .. c:type:: MACHINE_THUMB .. c:type:: MACHINE_WCEMIPSV2 *Example: pe.machine == pe.MACHINE_AMD64* .. c:type:: checksum .. versionadded:: 3.6.0 Integer with the "PE checksum" as stored in the OptionalHeader .. c:type:: calculate_checksum .. versionadded:: 3.6.0 Function that calculates the "PE checksum" *Example: pe.checksum == pe.calculate_checksum()* .. c:type:: subsystem Integer with one of the following values: .. c:type:: SUBSYSTEM_UNKNOWN .. c:type:: SUBSYSTEM_NATIVE .. c:type:: SUBSYSTEM_WINDOWS_GUI .. c:type:: SUBSYSTEM_WINDOWS_CUI .. c:type:: SUBSYSTEM_OS2_CUI .. c:type:: SUBSYSTEM_POSIX_CUI .. c:type:: SUBSYSTEM_NATIVE_WINDOWS .. c:type:: SUBSYSTEM_WINDOWS_CE_GUI .. c:type:: SUBSYSTEM_EFI_APPLICATION .. c:type:: SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER .. c:type:: SUBSYSTEM_EFI_RUNTIME_DRIVER .. c:type:: SUBSYSTEM_XBOX .. c:type:: SUBSYSTEM_WINDOWS_BOOT_APPLICATION *Example: pe.subsystem == pe.SUBSYSTEM_NATIVE* .. c:type:: timestamp PE timestamp, as an epoch integer. *Example: pe.timestamp >= 1424563200* .. c:type:: pointer_to_symbol_table .. versionadded:: 3.8.0 Value of IMAGE_FILE_HEADER::PointerToSymbolTable. Used when the PE image has COFF debug info. .. c:type:: number_of_symbols .. versionadded:: 3.8.0 Value of IMAGE_FILE_HEADER::NumberOfSymbols. Used when the PE image has COFF debug info. .. c:type:: size_of_optional_header .. versionadded:: 3.8.0 Value of IMAGE_FILE_HEADER::SizeOfOptionalHeader. This is real size of the optional header and reflects differences between 32-bit and 64-bit optional header and number of data directories. .. c:type:: opthdr_magic .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::Magic. .. c:type:: size_of_code .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfCode. This is the sum of raw data sizes in code sections. .. c:type:: size_of_initialized_data .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfInitializedData. .. c:type:: size_of_uninitialized_data Value of IMAGE_OPTIONAL_HEADER::SizeOfUninitializedData. .. c:type:: entry_point Entry point file offset or virtual address depending on whether YARA is scanning a file or process memory respectively. This is equivalent to the deprecated ``entrypoint`` keyword. .. c:type:: entry_point_raw Entry point raw value from the optional header of the PE. This value is not converted to a file offset or an RVA. .. versionadded:: 4.1.0 .. c:type:: base_of_code .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::BaseOfCode. .. c:type:: base_of_data .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::BaseOfData. This field only exists in 32-bit PE files. .. c:type:: image_base Image base relative virtual address. .. c:type:: section_alignment .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SectionAlignment. When Windows maps a PE image to memory, all raw sizes (including size of header) are aligned up to this value. .. c:type:: file_alignment .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::FileAlignment. All raw data sizes of sections in the PE image are aligned to this value. .. c:type:: win32_version_value .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::Win32VersionValue. .. c:type:: size_of_image .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfImage. This is the total virtual size of header and all sections. .. c:type:: size_of_headers .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfHeaders. This is the raw data size of the PE headers including DOS header, file header, optional header and all section headers. When PE is mapped to memory, this value is subject to aligning up to SectionAlignment. .. c:type:: characteristics Bitmap with PE FileHeader characteristics. Individual characteristics can be inspected by performing a bitwise AND operation with the following constants: .. c:type:: RELOCS_STRIPPED Relocation info stripped from file. .. c:type:: EXECUTABLE_IMAGE File is executable (i.e. no unresolved external references). .. c:type:: LINE_NUMS_STRIPPED Line numbers stripped from file. .. c:type:: LOCAL_SYMS_STRIPPED Local symbols stripped from file. .. c:type:: AGGRESIVE_WS_TRIM Aggressively trim working set .. c:type:: LARGE_ADDRESS_AWARE App can handle >2gb addresses .. c:type:: BYTES_REVERSED_LO Bytes of machine word are reversed. .. c:type:: MACHINE_32BIT 32 bit word machine. .. c:type:: DEBUG_STRIPPED Debugging info stripped from file in .DBG file .. c:type:: REMOVABLE_RUN_FROM_SWAP If Image is on removable media, copy and run from the swap file. .. c:type:: NET_RUN_FROM_SWAP If Image is on Net, copy and run from the swap file. .. c:type:: SYSTEM System File. .. c:type:: DLL File is a DLL. .. c:type:: UP_SYSTEM_ONLY File should only be run on a UP machine .. c:type:: BYTES_REVERSED_HI Bytes of machine word are reversed. *Example: pe.characteristics & pe.DLL* .. c:type:: linker_version An object with two integer attributes, one for each major and minor linker version. .. c:member:: major Major linker version. .. c:member:: minor Minor linker version. .. c:type:: os_version An object with two integer attributes, one for each major and minor OS version. .. c:member:: major Major OS version. .. c:member:: minor Minor OS version. .. c:type:: image_version An object with two integer attributes, one for each major and minor image version. .. c:member:: major Major image version. .. c:member:: minor Minor image version. .. c:type:: subsystem_version An object with two integer attributes, one for each major and minor subsystem version. .. c:member:: major Major subsystem version. .. c:member:: minor Minor subsystem version. .. c:type:: dll_characteristics Bitmap with PE OptionalHeader DllCharacteristics. Do not confuse these flags with the PE FileHeader Characteristics. Individual characteristics can be inspected by performing a bitwise AND operation with the following constants: .. c:type:: DYNAMIC_BASE File can be relocated - also marks the file as ASLR compatible .. c:type:: FORCE_INTEGRITY .. c:type:: NX_COMPAT Marks the file as DEP compatible .. c:type:: NO_ISOLATION .. c:type:: NO_SEH The file does not contain structured exception handlers, this must be set to use SafeSEH .. c:type:: NO_BIND .. c:type:: WDM_DRIVER Marks the file as a Windows Driver Model (WDM) device driver. .. c:type:: TERMINAL_SERVER_AWARE Marks the file as terminal server compatible .. c:type:: size_of_stack_reserve .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfStackReserve. This is the default amount of virtual memory that will be reserved for stack. .. c:type:: size_of_stack_commit .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfStackCommit. This is the default amount of virtual memory that will be allocated for stack. .. c:type:: size_of_heap_reserve .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfHeapReserve. This is the default amount of virtual memory that will be reserved for main process heap. .. c:type:: size_of_heap_commit .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::SizeOfHeapCommit. This is the default amount of virtual memory that will be allocated for main process heap. .. c:type:: loader_flags .. versionadded:: 3.8.0 Value of IMAGE_OPTIONAL_HEADER::LoaderFlags. .. c:type:: number_of_rva_and_sizes Value of IMAGE_OPTIONAL_HEADER::NumberOfRvaAndSizes. This is the number of items in the IMAGE_OPTIONAL_HEADER::DataDirectory array. .. c:type:: data_directories .. versionadded:: 3.8.0 A zero-based array of data directories. Each data directory contains virtual address and length of the appropriate data directory. Each data directory has the following entries: .. c:member:: virtual_address Relative virtual address (RVA) of the PE data directory. If this is zero, then the data directory is missing. Note that for digital signature, this is the file offset, not RVA. .. c:member:: size Size of the PE data directory, in bytes. The index for the data directory entry can be one of the following values: .. c:type:: IMAGE_DIRECTORY_ENTRY_EXPORT Data directory for exported functions. .. c:type:: IMAGE_DIRECTORY_ENTRY_IMPORT Data directory for import directory. .. c:type:: IMAGE_DIRECTORY_ENTRY_RESOURCE Data directory for resource section. .. c:type:: IMAGE_DIRECTORY_ENTRY_EXCEPTION Data directory for exception information. .. c:type:: IMAGE_DIRECTORY_ENTRY_SECURITY This is the raw file offset and length of the image digital signature. If the image has no embedded digital signature, this directory will contain zeros. .. c:type:: IMAGE_DIRECTORY_ENTRY_BASERELOC Data directory for image relocation table. .. c:type:: IMAGE_DIRECTORY_ENTRY_DEBUG Data directory for debug information. .. c:type:: IMAGE_DIRECTORY_ENTRY_TLS Data directory for image thread local storage. .. c:type:: IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG Data directory for image load configuration. .. c:type:: IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT Data directory for image bound import table. .. c:type:: IMAGE_DIRECTORY_ENTRY_IAT Data directory for image Import Address Table. .. c:type:: IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT Data directory for Delayed Import Table. Structure of the delayed import table is linker-dependent. Microsoft version of delayed imports is described in the souces "delayimp.h" and "delayimp.cpp", which can be found in MS Visual Studio 2008 CRT sources. .. c:type:: IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR Data directory for .NET headers. *Example: pe.data_directories[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].virtual_address != 0* .. c:type:: number_of_sections Number of sections in the PE. .. c:type:: sections .. versionadded:: 3.3.0 A zero-based array of section objects, one for each section the PE has. Individual sections can be accessed by using the [] operator. Each section object has the following attributes: .. c:member:: name Section name. .. c:member:: characteristics Section characteristics. .. c:member:: virtual_address Section virtual address. .. c:member:: virtual_size Section virtual size. .. c:member:: raw_data_offset Section raw offset. .. c:member:: raw_data_size Section raw size. .. c:member:: pointer_to_relocations .. versionadded:: 3.8.0 Value of IMAGE_SECTION_HEADER::PointerToRelocations. .. c:member:: pointer_to_line_numbers .. versionadded:: 3.8.0 Value of IMAGE_SECTION_HEADER::PointerToLinenumbers. .. c:member:: number_of_relocations .. versionadded:: 3.8.0 Value of IMAGE_SECTION_HEADER::NumberOfRelocations. .. c:member:: number_of_line_numbers .. versionadded:: 3.8.0 Value of IMAGE_SECTION_HEADER::NumberOfLineNumbers. *Example: pe.sections[0].name == ".text"* Individual section characteristics can be inspected using a bitwise AND operation with the following constants: .. c:type:: SECTION_CNT_CODE .. c:type:: SECTION_CNT_INITIALIZED_DATA .. c:type:: SECTION_CNT_UNINITIALIZED_DATA .. c:type:: SECTION_GPREL .. c:type:: SECTION_MEM_16BIT .. c:type:: SECTION_LNK_NRELOC_OVFL .. c:type:: SECTION_MEM_DISCARDABLE .. c:type:: SECTION_MEM_NOT_CACHED .. c:type:: SECTION_MEM_NOT_PAGED .. c:type:: SECTION_MEM_SHARED .. c:type:: SECTION_MEM_EXECUTE .. c:type:: SECTION_MEM_READ .. c:type:: SECTION_MEM_WRITE *Example: pe.sections[1].characteristics & pe.SECTION_CNT_CODE* .. c:type:: overlay .. versionadded:: 3.6.0 A structure containing the following integer members: .. c:member:: offset Overlay section offset. This is 0 for PE files that don't have overlaid data and undefined for non-PE files. .. c:member:: size Overlay section size. This is 0 for PE files that don't have overlaid data and undefined for non-PE files. *Example: uint8(pe.overlay.offset) == 0x0d and pe.overlay.size > 1024* .. c:type:: number_of_resources Number of resources in the PE. .. c:type:: resource_timestamp Resource timestamp. This is stored as an integer. .. c:type:: resource_version An object with two integer attributes, major and minor versions. .. c:member:: major Major resource version. .. c:member:: minor Minor resource version. .. c:type:: resources .. versionchanged:: 3.3.0 A zero-based array of resource objects, one for each resource the PE has. Individual resources can be accessed by using the [] operator. Each resource object has the following attributes: .. c:member:: rva The RVA of the resource data. .. c:member:: offset Offset for the resource data. This can be undefined if the RVA is invalid. .. c:member:: length Length of the resource data. .. c:member:: type Type of the resource (integer). .. c:member:: id ID of the resource (integer). .. c:member:: language Language of the resource (integer). .. c:member:: type_string Type of the resource as a string, if specified. .. c:member:: name_string Name of the resource as a string, if specified. .. c:member:: language_string Language of the resource as a string, if specified. All resources must have a type, id (name), and language specified. They can be either an integer or string, but never both, for any given level. *Example: pe.resources[0].type == pe.RESOURCE_TYPE_RCDATA* *Example: pe.resources[0].name_string == "F\\x00I\\x00L\\x00E\\x00"* Resource types can be inspected using the following constants: .. c:type:: RESOURCE_TYPE_CURSOR .. c:type:: RESOURCE_TYPE_BITMAP .. c:type:: RESOURCE_TYPE_ICON .. c:type:: RESOURCE_TYPE_MENU .. c:type:: RESOURCE_TYPE_DIALOG .. c:type:: RESOURCE_TYPE_STRING .. c:type:: RESOURCE_TYPE_FONTDIR .. c:type:: RESOURCE_TYPE_FONT .. c:type:: RESOURCE_TYPE_ACCELERATOR .. c:type:: RESOURCE_TYPE_RCDATA .. c:type:: RESOURCE_TYPE_MESSAGETABLE .. c:type:: RESOURCE_TYPE_GROUP_CURSOR .. c:type:: RESOURCE_TYPE_GROUP_ICON .. c:type:: RESOURCE_TYPE_VERSION .. c:type:: RESOURCE_TYPE_DLGINCLUDE .. c:type:: RESOURCE_TYPE_PLUGPLAY .. c:type:: RESOURCE_TYPE_VXD .. c:type:: RESOURCE_TYPE_ANICURSOR .. c:type:: RESOURCE_TYPE_ANIICON .. c:type:: RESOURCE_TYPE_HTML .. c:type:: RESOURCE_TYPE_MANIFEST For more information refer to: http://msdn.microsoft.com/en-us/library/ms648009(v=vs.85).aspx .. c:type:: version_info .. versionadded:: 3.2.0 Dictionary containing the PE's version information. Typical keys are: ``Comments`` ``CompanyName`` ``FileDescription`` ``FileVersion`` ``InternalName`` ``LegalCopyright`` ``LegalTrademarks`` ``OriginalFilename`` ``ProductName`` ``ProductVersion`` For more information refer to: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646987(v=vs.85).aspx *Example: pe.version_info["CompanyName"] contains "Microsoft"* .. c:type:: number_of_signatures Number of authenticode signatures in the PE. .. c:type:: signatures A zero-based array of signature objects, one for each authenticode signature in the PE file. Usually PE files have a single signature. .. c:member:: thumbprint .. versionadded:: 3.8.0 A string containing the thumbprint of the signature. .. c:member:: issuer A string containing information about the issuer. These are some examples:: "/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/CN=Microsoft Code Signing PCA" "/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Code Signing 2010 CA" "/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2" .. c:member:: subject A string containing information about the subject. .. c:member:: version Version number. .. c:member:: algorithm String representation of the algorithm used for this signature. Usually "sha1WithRSAEncryption". It depends on the X.509 and PKCS#7 implementationss and possibly their versions, consider using algorithm_oid instead. .. c:member:: algorithm_oid Object ID of the algorithm used for this signature, expressed in numeric ASN.1 dot notation. The name contained in algorithm is derived from this value. The object id is expected to be stable across X.509 and PKCS#7 implementations and their versions. For example, when using the current OpenSSL-based implementation:: algorithm_oid == "1.2.840.113549.1.1.11" is functionally equivalent to:: algorithm == "sha1WithRSAEncryption" .. c:member:: serial A string containing the serial number. This is an example:: "52:00:e5:aa:25:56:fc:1a:86:ed:96:c9:d4:4b:33:c7" .. c:member:: not_before Unix timestamp on which the validity period for this signature begins. .. c:member:: not_after Unix timestamp on which the validity period for this signature ends. .. c:member:: valid_on(timestamp) Function returning true if the signature was valid on the date indicated by *timestamp*. The following sentence:: pe.signatures[n].valid_on(timestamp) Is equivalent to:: timestamp >= pe.signatures[n].not_before and timestamp <= pe.signatures[n].not_after .. c:type:: rich_signature Structure containing information about the PE's rich signature as documented `here `_. .. c:member:: offset Offset where the rich signature starts. It will be undefined if the file doesn't have a rich signature. .. c:member:: length Length of the rich signature, not including the final "Rich" marker. .. c:member:: key Key used to encrypt the data with XOR. .. c:member:: raw_data Raw data as it appears in the file. .. c:member:: clear_data Data after being decrypted by XORing it with the key. .. c:function:: version(version, [toolid]) .. versionadded:: 3.5.0 Function returning a sum of count values of all matching *version* records. Provide the optional *toolid* argument to only match when both match for one entry. More information can be found here: http://www.ntcore.com/files/richsign.htm Note: Prior to version *3.11.0*, this function returns only a boolean value (0 or 1) if the given *version* and optional *toolid* is present in an entry. *Example: pe.rich_signature.version(24215, 261) == 61* .. c:function:: toolid(toolid, [version]) .. versionadded:: 3.5.0 Function returning a sum of count values of all matching *toolid* records. Provide the optional *version* argument to only match when both match for one entry. More information can be found here: http://www.ntcore.com/files/richsign.htm Note: Prior to version *3.11.0*, this function returns only a boolean value (0 or 1) if the given *toolid* and optional *version* is present in an entry. *Example: pe.rich_signature.toolid(170, 40219) >= 99* .. c:type:: pdb_path .. versionadded:: 4.0.0 Path of the PDB file for this PE if present. *Example: pe.pdb_path == "D:\\workspace\\2018_R9_RelBld\\target\\checkout\\custprof\\Release\\custprof.pdb"* .. c:function:: exports(function_name) Function returning true if the PE exports *function_name* or false otherwise. *Example: pe.exports("CPlApplet")* .. c:function:: exports(ordinal) .. versionadded:: 3.6.0 Function returning true if the PE exports *ordinal* or false otherwise. *Example: pe.exports(72)* .. c:function:: exports(/regular_expression/) .. versionadded:: 3.7.1 Function returning true if the PE exports *regular_expression* or false otherwise. *Example: pe.exports(/^AXS@@/)* .. c:function:: exports_index(function_name) .. versionadded:: 4.0.0 Function returning the index into the export_details array where the named function is, undefined otherwise. *Example: pe.exports_index("CPlApplet")* .. c:function:: exports_index(ordinal) .. versionadded:: 4.0.0 Function returning the index into the export_details array where the exported ordinal is, undefined otherwise. *Example: pe.exports_index(72)* .. c:function:: exports_index(/regular_expression/) .. versionadded:: 4.0.0 Function returning the first index into the export_details array where the regular expression matches the exported name, undefined otherwise. *Example: pe.exports_index(/^ERS@@/)* .. c:type:: number_of_exports .. versionadded:: 3.6.0 Number of exports in the PE. .. c:type:: export_details .. versionadded:: 4.0.0 Array of structures containing information about the PE's exports. .. c:member:: offset Offset where the exported function starts. .. c:member:: name Name of the exported function. It will be undefined if the function has no name. .. c:member:: forward_name The name of the function where this export forwards to. It will be undefined if the export is not a forwarding export. .. c:member:: ordinal The ordinal of the exported function, after the ordinal base has been applied to it. .. c:type:: dll_name .. versionadded:: 4.0.0 The name of the DLL, if it exists in the export directory. .. c:type:: export_timestamp .. versionadded:: 4.0.0 The timestamp the export data was created.. .. c:type:: number_of_imports .. versionadded:: 3.6.0 Number of imported DLLs in the PE. .. c:type:: number_of_imported_functions .. versionadded:: 4.1.0 Number of imported functions in the PE. .. c:function:: imports(dll_name, function_name) Function returning true if the PE imports *function_name* from *dll_name*, or false otherwise. *dll_name* is case insensitive. *Example: pe.imports("kernel32.dll", "WriteProcessMemory")* .. c:function:: imports(dll_name) .. versionadded:: 3.5.0 .. versionchanged:: 4.0.0 Function returning the number of functions from the *dll_name*, in the PE imports. *dll_name* is case insensitive. Note: Prior to version 4.0.0, this function returned only a boolean value indicating if the given DLL name was found in the PE imports. This change is backward compatible, as any number larger than 0 also evaluates as true. *Examples: pe.imports("kernel32.dll"), pe.imports("kernel32.dll") == 10* .. c:function:: imports(dll_name, ordinal) .. versionadded:: 3.5.0 Function returning true if the PE imports *ordinal* from *dll_name*, or false otherwise. *dll_name* is case insensitive. *Example: pe.imports("WS2_32.DLL", 3)* .. c:function:: imports(dll_regexp, function_regexp) .. versionadded:: 3.8.0 .. versionchanged:: 4.0.0 Function returning the number of functions from the PE imports where a function name matches *function_regexp* and a DLL name matches *dll_regexp*. Both *dll_regexp* and *function_regexp* are case sensitive unless you use the "/i" modifier in the regexp, as shown in the example below. Note: Prior to version 4.0.0, this function returned only a boolean value indicating if matching import was found or not. This change is backward compatible, as any number larger than 0 also evaluates as true. *Example: pe.imports(/kernel32\.dll/i, /(Read|Write)ProcessMemory/) == 2* .. c:function:: locale(locale_identifier) .. versionadded:: 3.2.0 Function returning true if the PE has a resource with the specified locale identifier. Locale identifiers are 16-bit integers and can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd318693(v=vs.85).aspx *Example: pe.locale(0x0419) // Russian (RU)* .. c:function:: language(language_identifier) .. versionadded:: 3.2.0 Function returning true if the PE has a resource with the specified language identifier. Language identifiers are 8-bit integers and can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd318693(v=vs.85).aspx *Example: pe.language(0x0A) // Spanish* .. c:function:: imphash() .. versionadded:: 3.2.0 Function returning the import hash or imphash for the PE. The imphash is an MD5 hash of the PE's import table after some normalization. The imphash for a PE can be also computed with `pefile `_ and you can find more information in `Mandiant's blog `_. The returned hash string is always in lowercase. *Example: pe.imphash() == "b8bb385806b89680e13fc0cf24f4431e"* .. c:function:: section_index(name) Function returning the index into the sections array for the section that has *name*. *name* is case sensitive. *Example: pe.section_index(".TEXT")* .. c:function:: section_index(addr) .. versionadded:: 3.3.0 Function returning the index into the sections array for the section that has *addr*. *addr* can be an offset into the file or a memory address. *Example: pe.section_index(pe.entry_point)* .. c:function:: is_pe() .. versionadded:: 3.8.0 Return true if the file is a PE. *Example: pe.is_pe()* .. c:function:: is_dll() .. versionadded:: 3.5.0 Function returning true if the PE is a DLL. *Example: pe.is_dll()* .. c:function:: is_32bit() .. versionadded:: 3.5.0 Function returning true if the PE is 32bits. *Example: pe.is_32bit()* .. c:function:: is_64bit() .. versionadded:: 3.5.0 Function returning true if the PE is 64bits. *Example: pe.is_64bit()* .. c:function:: rva_to_offset(addr) .. versionadded:: 3.6.0 Function returning the file offset for RVA *addr*. Be careful to pass relative addresses here and not absolute addresses, like `pe.entry_point` when scanning a process. *Example: pe.rva_to_offset(pe.sections[0].virtual_address) == pe.sections[0].raw_data_offset* This example will make sure the offset for the virtual address in the first section equals the file offset for that section. yara-4.1.3/docs/modules/time.rst000066400000000000000000000004731413423160300165440ustar00rootroot00000000000000 .. _time-module: ############ Time module ############ .. versionadded:: 3.7.0 The Time module allows you to use temporal conditions in your YARA rules. .. c:function:: now() Function returning an integer which is the number of seconds since January 1, 1970. *Example: pe.timestamp > time.now()* yara-4.1.3/docs/writingmodules.rst000066400000000000000000000764631413423160300172260ustar00rootroot00000000000000.. _writing-modules: ************************ Writing your own modules ************************ For the first time ever, in YARA 3.0 you can extend its features to express more complex and refined conditions. YARA 3.0 does this by employing modules, which you can use to define data structures and functions, which can be later used from within your rules. You can see some examples of what a module can do in the :ref:`using-modules` section. The purpose of the following sections is to teach you how to create your own modules for giving YARA that cool feature you always dreamed of. The "Hello World!" module ========================= Modules are written in C and built into YARA as part of the compiling process. In order to create your own modules you must be familiar with the C programming language and how to configure and build YARA from source code. You don't need to understand how YARA does its magic; YARA exposes a simple API for modules, which is all you need to know. The source code for your module must reside in the *libyara/modules* directory of the source tree. It's recommended to use the module name as the file name for the source file, if your module's name is *foo* its source file should be *foo.c*. In the *libyara/modules* directory you'll find a *demo.c* file we'll use as our starting point. The file looks like this: .. code-block:: c #include #define MODULE_NAME demo begin_declarations; declare_string("greeting"); end_declarations; int module_initialize( YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize( YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { set_string("Hello World!", module_object, "greeting"); return ERROR_SUCCESS; } int module_unload( YR_OBJECT* module_object) { return ERROR_SUCCESS; } #undef MODULE_NAME Let's start dissecting the source code so you can understand every detail. The first line in the code is: .. code-block:: c #include The *modules.h* header file is where the definitions for YARA's module API reside, therefore this include directive is required in all your modules. The second line is: .. code-block:: c #define MODULE_NAME demo This is how you define the name of your module and is also required. Every module must define its name at the start of the source code. Module names must be unique among the modules built into YARA. Then follows the declaration section: .. code-block:: c begin_declarations; declare_string("greeting"); end_declarations; Here is where the module declares the functions and data structures that will be available for your YARA rules. In this case we are declaring just a string variable named *greeting*. We are going to discuss these concepts in greater detail in the :ref:`declaration-section`. After the declaration section you'll find a pair of functions: .. code-block:: c int module_initialize( YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize( YR_MODULE* module) { return ERROR_SUCCESS; } The ``module_initialize`` function is called during YARA's initialization while its counterpart ``module_finalize`` is called while finalizing YARA. These functions allow you to initialize and finalize any global data structure you may need to use in your module. Then comes the ``module_load`` function: .. code-block:: c int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { set_string("Hello World!", module_object, "greeting"); return ERROR_SUCCESS; } This function is invoked once for each scanned file, but only if the module is imported by some rule with the ``import`` directive. The ``module_load`` function is where your module has the opportunity to inspect the file being scanned, parse or analyze it in the way preferred, and then populate the data structures defined in the declarations section. In this example the ``module_load`` function doesn't inspect the file content at all, it just assigns the string, "Hello World!" to the variable *greeting* declared before. And finally, we have the ``module_unload`` function: .. code-block:: c int module_unload( YR_OBJECT* module_object) { return ERROR_SUCCESS; } For each call to ``module_load`` there is a corresponding call to ``module_unload``. This function allows your module to free any resource allocated during ``module_load``. There's nothing to free in this case, so the function just returns ``ERROR_SUCCESS``. Both ``module_load`` and ``module_unload`` should return ``ERROR_SUCCESS`` to indicate that everything went fine. If a different value is returned the scanning will be aborted and an error reported to the user. Building our "Hello World!" --------------------------- Modules are not magically built into YARA just by dropping their source code into the *libyara/modules* directory, you must follow two further steps in order to get them to work. The first step is adding your module to the *module_list* file also found in the *libyara/modules* directory. The *module_list* file looks like this:: MODULE(tests) MODULE(pe) #ifdef CUCKOO_MODULE MODULE(cuckoo) #endif You must add a line *MODULE()* with the name of your module to this file. In our case the resulting *module_list* is:: MODULE(tests) MODULE(pe) #ifdef CUCKOO_MODULE MODULE(cuckoo) #endif MODULE(demo) The second step is modifying the *Makefile.am* to tell the *make* program that the source code for your module must be compiled and linked into YARA. At the very beginning of *libyara/Makefile.am* you'll find this:: MODULES = modules/tests.c MODULES += modules/pe.c if CUCKOO_MODULE MODULES += modules/cuckoo.c endif Just add a new line for your module:: MODULES = modules/tests.c MODULES += modules/pe.c if CUCKOO_MODULE MODULES += modules/cuckoo.c endif MODULES += modules/demo.c And that's all! Now you're ready to build YARA with your brand-new module included. Just go to the source tree root directory and type as always:: make sudo make install Now you should be able to create a rule like this: .. code-block:: yara import "demo" rule HelloWorld { condition: demo.greeting == "Hello World!" } Any file scanned with this rule will match the ``HelloWord`` because ``demo.greeting == "Hello World!"`` is always true. .. _declaration-section: The declaration section ======================= The declaration section is where you declare the variables, structures and functions that will be available for your YARA rules. Every module must contain a declaration section like this:: begin_declarations; end_declarations; Basic types ----------- Within the declaration section you can use ``declare_string()``, ``declare_integer()`` and ``declare_float()`` to declare string, integer, or float variables respectively. For example:: begin_declarations; declare_integer("foo"); declare_string("bar"); declare_float("baz"); end_declarations; .. note:: Floating-point variables require YARA version 3.3.0 or later. Variable names can't contain characters other than letters, numbers and underscores. These variables can be used later in your rules at any place where an integer or string is expected. Supposing your module name is "mymodule", they can be used like this:: mymodule.foo > 5 mymodule.bar matches /someregexp/ Structures ---------- Your declarations can be organized in a more structured way:: begin_declarations; declare_integer("foo"); declare_string("bar"); declare_float("baz"); begin_struct("some_structure"); declare_integer("foo"); begin_struct("nested_structure"); declare_integer("bar"); end_struct("nested_structure"); end_struct("some_structure"); begin_struct("another_structure"); declare_integer("foo"); declare_string("bar"); declare_string("baz"); declare_float("tux"); end_struct("another_structure"); end_declarations; In this example we're using ``begin_struct()`` and ``end_struct()`` to delimit two structures named *some_structure* and *another_structure*. Within the structure delimiters you can put any other declarations you want, including another structure declaration. Also notice that members of different structures can have the same name, but members within the same structure must have unique names. When referring to these variables from your rules it would be like this:: mymodule.foo mymodule.some_structure.foo mymodule.some_structure.nested_structure.bar mymodule.another_structure.baz Arrays ------ In the same way you declare individual strings, integers, floats or structures, you can declare arrays of them:: begin_declarations; declare_integer_array("foo"); declare_string_array("bar"); declare_float_array("baz"); begin_struct_array("struct_array"); declare_integer("foo"); declare_string("bar"); end_struct_array("struct_array"); end_declarations; Individual values in the array are referenced like in most programming languages:: foo[0] bar[1] baz[3] struct_array[4].foo struct_array[1].bar Arrays are zero-based and don't have a fixed size, they will grow as needed when you start initializing its values. Dictionaries ------------ .. versionadded:: 3.2.0 You can also declare dictionaries of integers, floats, strings, or structures:: begin_declarations; declare_integer_dictionary("foo"); declare_string_dictionary("bar"); declare_float_dictionary("baz") begin_struct_dictionary("struct_dict"); declare_integer("foo"); declare_string("bar"); end_struct_dictionary("struct_dict"); end_declarations; Individual values in the dictionary are accessed by using a string key:: foo["somekey"] bar["anotherkey"] baz["yetanotherkey"] struct_dict["k1"].foo struct_dict["k1"].bar .. _declaring-functions: Functions --------- One of the more powerful features of YARA modules is the possibility of declaring functions that can be later invoked from your rules. Functions must appear in the declaration section in this way:: declare_function(, , , ); ** is the name that will be used in your YARA rules to invoke the function. ** is a string containing one character per function argument, where the character indicates the type of the argument. Functions can receive four different types of arguments: string, integer, float and regular expression, denoted by characters: **s**, **i**, **f** and **r** respectively. If your function receives two integers ** must be *"ii"*, if it receives an integer as the first argument and a string as the second one ** must be *"is"*, if it receives three strings and a float ** must be "*sssf*". ** is a string with a single character indicating the return type. Possible return types are string (*"s"*) integer (*"i"*) and float (*"f"*). ** is the identifier for the actual implementation of your function. Here you have a full example: .. code-block:: c define_function(isum) { int64_t a = integer_argument(1); int64_t b = integer_argument(2); return_integer(a + b); } define_function(fsum) { double a = float_argument(1); double b = float_argument(2); return_integer(a + b); } begin_declarations; declare_function("sum", "ii", "i", sum); end_declarations; As you can see in the example above, your function code must be defined before the declaration section, like this:: define_function() { ...your code here } Functions can be overloaded as in C++ and other programming languages. You can declare two functions with the same name as long as they differ in the type or number of arguments. One example of overloaded functions can be found in the :ref:`hash-module`, it has two functions for calculating MD5 hashes, one receiving an offset and length within the file and another one receiving a string:: begin_declarations; declare_function("md5", "ii", "s", data_md5); declare_function("md5", "s", "s", string_md5); end_declarations; We are going to discuss function implementation more in depth in the :ref:`implementing-functions` section. Initialization and finalization =============================== Every module must implement two functions for initialization and finalization: ``module_initialize`` and ``module_finalize``. The former is called during YARA's initialization by :c:func:`yr_initialize` while the latter is called during finalization by :c:func:`yr_finalize`. Both functions are invoked whether or not the module is being imported by some rule. These functions give your module an opportunity to initialize any global data structure it may need, but most of the time they are just empty functions: .. code-block:: c int module_initialize( YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize( YR_MODULE* module) { return ERROR_SUCCESS; } Any returned value different from ``ERROR_SUCCESS`` will abort YARA's execution. Implementing the module's logic =============================== Besides ``module_initialize`` and ``module_finalize`` every module must implement two other functions which are called by YARA during the scanning of a file or process memory space: ``module_load`` and ``module_unload``. Both functions are called once for each scanned file or process, but only if the module was imported by means of the ``import`` directive. If the module is not imported by some rule neither ``module_load`` nor ``module_unload`` will be called. The ``module_load`` function has the following prototype: .. code-block:: c int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) The ``context`` argument contains information relative to the current scan, including the data being scanned. The ``module_object`` argument is a pointer to a ``YR_OBJECT`` structure associated with the module. Each structure, variable or function declared in a YARA module is represented by a ``YR_OBJECT`` structure. These structures form a tree whose root is the module's ``YR_OBJECT`` structure. If you have the following declarations in a module named *mymodule*:: begin_declarations; declare_integer("foo"); begin_struct("bar"); declare_string("baz"); end_struct("bar"); end_declarations; Then the tree will look like this:: YR_OBJECT(type=OBJECT_TYPE_STRUCT, name="mymodule") | |_ YR_OBJECT(type=OBJECT_TYPE_INTEGER, name="foo") | |_ YR_OBJECT(type=OBJECT_TYPE_STRUCT, name="bar") | |_ YR_OBJECT(type=OBJECT_TYPE_STRING, name="baz") Notice that both *bar* and *mymodule* are of the same type ``OBJECT_TYPE_STRUCT``, which means that the ``YR_OBJECT`` associated with the module is just another structure like *bar*. In fact, when you write in your rules something like ``mymodule.foo`` you're performing a field lookup in a structure in the same way that ``bar.baz`` does. In summary, the ``module_object`` argument allows you to access every variable, structure or function declared by the module by providing a pointer to the root of the objects tree. The ``module_data`` argument is a pointer to any additional data passed to the module, and ``module_data_size`` is the size of that data. Not all modules require additional data, most of them rely on the data being scanned alone, but a few of them require more information as input. The :ref:`cuckoo-module` is a good example of this, it receives a behavior report associated with PE files being scanned which is passed in the ``module_data`` and ``module_data_size`` arguments. For more information on how to pass additional data to your module take a look at the ``-x`` argument in :ref:`command-line`. .. _accessing-scanned-data: Accessing the scanned data -------------------------- Most YARA modules need to access the file or process memory being scanned to extract information from it. The data being scanned is sent to the module in the ``YR_SCAN_CONTEXT`` structure passed to the ``module_load`` function. The data is sometimes sliced in blocks, therefore your module needs to iterate over the blocks by using the ``foreach_memory_block`` macro: .. code-block:: c int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; foreach_memory_block(context, block) { ..do something with the current memory block } } Each memory block is represented by a ``YR_MEMORY_BLOCK`` structure with the following attributes: .. c:type:: YR_MEMORY_BLOCK_FETCH_DATA_FUNC fetch_data Pointer to a function returning a pointer to the block's data. .. c:type:: size_t size Size of the data block. .. c:type:: size_t base Base offset/address for this block. If a file is being scanned this field contains the offset within the file where the block begins, if a process memory space is being scanned this contains the virtual address where the block begins. The blocks are always iterated in the same order as they appear in the file or process memory. In the case of files the first block will contain the beginning of the file. Actually, a single block will contain the whole file's content in most cases, but you can't rely on that while writing your code. For very big files YARA could eventually split the file into two or more blocks, and your module should be prepared to handle that. The story is very different for processes. While scanning a process memory space your module will definitely receive a large number of blocks, one for each committed memory region in the process address space. However, there are some cases where you don't actually need to iterate over the blocks. If your module just parses the header of some file format you can safely assume that the whole header is contained within the first block (put some checks in your code nevertheless). In those cases you can use the ``first_memory_block`` macro: .. code-block:: c int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; const uint8_t* block_data; block = first_memory_block(context); block_data = block->fetch_data(block) if (block_data != NULL) { ..do something with the memory block } } In the previous example you can also see how to use the ``fetch_data`` function. This function, which is a member of the ``YR_MEMORY_BLOCK`` structure, receives a pointer to the same block (as a ``self`` or ``this`` pointer) and returns a pointer to the block's data. Your module doesn't own the memory pointed to by this pointer, freeing that memory is not your responsibility. However keep in mind that the pointer is valid only until you ask for the next memory block. As long as you use the pointer within the scope of a ``foreach_memory_block`` you are on the safe side. Also take into account that ``fetch_data`` can return a NULL pointer, your code must be prepared for that case. .. code-block:: c const uint8_t* block_data; foreach_memory_block(context, block) { block_data = block->fetch_data(block); if (block_data != NULL) { // using block_data is safe here. } } // the memory pointed to by block_data can be already freed here. Setting variable's values ------------------------- The ``module_load`` function is where you assign values to the variables declared in the declarations section, once you've parsed or analyzed the scanned data and/or any additional module's data. This is done by using the ``set_float``, ``set_integer``, and ``set_string`` functions: .. c:function:: void set_float(double value, YR_OBJECT* object, const char* field, ...) .. c:function:: void set_integer(int64_t value, YR_OBJECT* object, const char* field, ...) .. c:function:: void set_string(const char* value, YR_OBJECT* object, const char* field, ...) These functions receive a value to be assigned to the variable, a pointer to a ``YR_OBJECT`` representing the variable itself or some ancestor of that variable, a field descriptor, and additional arguments as defined by the field descriptor. If we are assigning the value to the variable represented by ``object`` itself, then the field descriptor must be ``NULL``. For example, assuming that ``object`` points to a ``YR_OBJECT`` structure corresponding to some integer variable, we can set the value for that integer variable with: .. code-block:: c set_integer(, object, NULL); The field descriptor is used when you want to assign the value to some descendant of ``object``. For example, consider the following declarations:: begin_declarations; begin_struct("foo"); declare_string("bar"); begin_struct("baz"); declare_integer("qux"); end_struct("baz"); end_struct("foo"); end_declarations; If ``object`` points to the ``YR_OBJECT`` associated with the ``foo`` structure you can set the value for the ``bar`` string like this: .. code-block:: c set_string(, object, "bar"); And the value for ``qux`` like this: .. code-block:: c set_integer(, object, "baz.qux"); Do you remember that the ``module_object`` argument for ``module_load`` was a pointer to a ``YR_OBJECT``? Do you remember that this ``YR_OBJECT`` is a structure just like ``bar`` is? Well, you could also set the values for ``bar`` and ``qux`` like this: .. code-block:: c set_string(, module_object, "foo.bar"); set_integer(, module_object, "foo.baz.qux"); But what happens with arrays? How can I set the value for array items? If you have the following declarations:: begin_declarations; declare_integer_array("foo"); begin_struct_array("bar") declare_string("baz"); declare_integer_array("qux"); end_struct_array("bar"); end_declarations; Then the following statements are all valid: .. code-block:: c set_integer(, module, "foo[0]"); set_integer(, module, "foo[%i]", 2); set_string(, module, "bar[%i].baz", 5); set_string(, module, "bar[0].qux[0]"); set_string(, module, "bar[0].qux[%i]", 0); set_string(, module, "bar[%i].qux[%i]", 100, 200); Those ``%i`` in the field descriptor are replaced by the additional integer arguments passed to the function. This works in the same way as ``printf`` in C programs, but the only format specifiers accepted are ``%i`` and ``%s``, for integer and string arguments respectively. The ``%s`` format specifier is used for assigning values to a certain key in a dictionary: .. code-block:: c set_integer(, module, "foo[\"key\"]"); set_integer(, module, "foo[%s]", "key"); set_string(, module, "bar[%s].baz", "another_key"); If you don't explicitly assign a value to a declared variable, array or dictionary item it will remain in an undefined state. That's not a problem at all, and is even useful in many cases. For example, if your module parses files from a certain format and it receives one from a different format, you can safely leave all your variables undefined instead of assigning them bogus values that don't make sense. YARA will handle undefined values in rule conditions as described in :ref:`using-modules`. In addition to the ``set_float``, ``set_integer``, and ``set_string`` functions, you have their ``get_float``, ``get_integer``, and ``get_string`` counterparts. As the names suggest, they are used for getting the value of a variable, which can be useful in the implementation of your functions to retrieve values previously stored by ``module_load``. .. c:function:: double get_float(YR_OBJECT* object, const char* field, ...) .. c:function:: int64_t get_integer(YR_OBJECT* object, const char* field, ...) .. c:function:: SIZED_STRING* get_string(YR_OBJECT* object, const char* field, ...) There's also a function to get any ``YR_OBJECT`` in the objects tree: .. c:function:: YR_OBJECT* get_object(YR_OBJECT* object, const char* field, ...) Here is a little exam... Are the following two lines equivalent? Why? .. code-block:: c set_integer(1, get_object(module_object, "foo.bar"), NULL); set_integer(1, module_object, "foo.bar"); .. _storing-data-for-later-use: Storing data for later use -------------------------- Sometimes the information stored directly in your variables by means of ``set_integer`` and ``set_string`` is not enough. You may need to store more complex data structures or information that doesn't need to be exposed to YARA rules. Storing information is essential when your module exports functions to be used in YARA rules. The implementation of these functions usually require to access information generated by ``module_load`` which must kept somewhere. You may be tempted to define global variables to store the required information, but this would make your code non-thread-safe. The correct approach is using the ``data`` field of the ``YR_OBJECT`` structures. Each ``YR_OBJECT`` has a ``void* data`` field which can be safely used by your code to store a pointer to any data you may need. A typical pattern is using the ``data`` field of the module's ``YR_OBJECT``, like in the following example: .. code-block:: c typedef struct _MY_DATA { int some_integer; } MY_DATA; int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { module->data = yr_malloc(sizeof(MY_DATA)); ((MY_DATA*) module_object->data)->some_integer = 0; return ERROR_SUCCESS; } Don't forget to release the allocated memory in the ``module_unload`` function: .. code-block:: cpp int module_unload( YR_OBJECT* module_object) { yr_free(module_object->data); return ERROR_SUCCESS; } .. warning:: Don't use global variables for storing data. Functions in a module can be invoked from different threads at the same time and data corruption or misbehavior can occur. .. _implementing-functions: More about functions ==================== We already showed how to declare a function in :ref:`The declaration section `. Here we are going to discuss how to provide an implementation for them. Function arguments ------------------ Within the function's code you get its arguments by using ``integer_argument(n)``, ``float_argument(n)``, ``regexp_argument(n)``, ``string_argument(n)`` or ``sized_string_argument(n)`` depending on the type of the argument, where *n* is the 1-based argument's number. ``string_argument(n)`` can be used when your function expects to receive a NULL-terminated C string, if your function can receive arbitrary binary data possibly containing NULL characters you must use ``sized_string_argument(n)``. Here you have some examples: .. code-block:: c int64_t arg_1 = integer_argument(1); RE* arg_2 = regexp_argument(2); char* arg_3 = string_argument(3); SIZED_STRING* arg_4 = sized_string_argument(4); double arg_5 = float_argument(1); The C type for integer arguments is ``int64_t``, for float arguments is ``double``, for regular expressions is ``RE*``, for NULL-terminated strings is ``char*`` and for strings possibly containing NULL characters is ``SIZED_STRING*``. ``SIZED_STRING`` structures have the following attributes: .. c:type:: SIZED_STRING .. c:member:: length String's length. .. c:member:: c_string ``char*`` pointing to the string content. Return values ------------- Functions can return three types of values: strings, integers and floats. Instead of using the C *return* statement you must use ``return_string(x)``, ``return_integer(x)`` or ``return_float(x)`` to return from a function, depending on the function's return type. In all cases *x* is a constant, variable, or expression evaluating to ``char*``, ``int64_t`` or ``double`` respectively. You can use ``return_string(YR_UNDEFINED)``, ``return_float(YR_UNDEFINED)`` and ``return_integer(YR_UNDEFINED)`` to return undefined values from the function. This is useful in many situations, for example if the arguments passed to the functions don't make sense, or if your module expects a particular file format and the scanned file is from another format, or in any other case where your function can't a return a valid value. .. warning:: Don't use the C *return* statement for returning from a function. The returned value will be interpreted as an error code. Accessing objects ----------------- While writing a function we sometimes need to access values previously assigned to the module's variables, or additional data stored in the ``data`` field of ``YR_OBJECT`` structures as discussed earlier in :ref:`storing-data-for-later-use`. But for that we need a way to get access to the corresponding ``YR_OBJECT`` first. There are two functions to do that: ``module()`` and ``parent()``. The ``module()`` function returns a pointer to the top-level ``YR_OBJECT`` corresponding to the module, the same one passed to the ``module_load`` function. The ``parent()`` function returns a pointer to the ``YR_OBJECT`` corresponding to the structure where the function is contained. For example, consider the following code snippet: .. code-block:: c define_function(f1) { YR_OBJECT* module = module(); YR_OBJECT* parent = parent(); // parent == module; } define_function(f2) { YR_OBJECT* module = module(); YR_OBJECT* parent = parent(); // parent != module; } begin_declarations; declare_function("f1", "i", "i", f1); begin_struct("foo"); declare_function("f2", "i", "i", f2); end_struct("foo"); end_declarations; In ``f1`` the ``module`` variable points to the top-level ``YR_OBJECT`` as well as the ``parent`` variable, because the parent for ``f1`` is the module itself. In ``f2`` however the ``parent`` variable points to the ``YR_OBJECT`` corresponding to the ``foo`` structure while ``module`` points to the top-level ``YR_OBJECT`` as before. Scan context ------------ From within a function you can also access the ``YR_SCAN_CONTEXT`` structure discussed earlier in :ref:`accessing-scanned-data`. This is useful for functions which needs to inspect the file or process memory being scanned. This is how you get a pointer to the ``YR_SCAN_CONTEXT`` structure: .. code-block:: c YR_SCAN_CONTEXT* context = scan_context(); yara-4.1.3/docs/writingrules.rst000066400000000000000000001455561413423160300167100ustar00rootroot00000000000000******************* Writing YARA rules ******************* YARA rules are easy to write and understand, and they have a syntax that resembles the C language. Here is the simplest rule that you can write for YARA, which does absolutely nothing: .. code-block:: yara rule dummy { condition: false } Each rule in YARA starts with the keyword ``rule`` followed by a rule identifier. Identifiers must follow the same lexical conventions of the C programming language, they can contain any alphanumeric character and the underscore character, but the first character cannot be a digit. Rule identifiers are case sensitive and cannot exceed 128 characters. The following keywords are reserved and cannot be used as an identifier: .. list-table:: YARA keywords :widths: 10 10 10 10 10 10 10 10 * - all - and - any - ascii - at - base64 - base64wide - condition * - contains - endswith - entrypoint - false - filesize - for - fullword - global * - import - icontains - iendswith - in - include - int16 - int16be - int32 * - int32be - int8 - int8be - istartswith - matches - meta - nocase - not * - of - or - private - rule - startswith - strings - them - true * - uint16 - uint16be - uint32 - uint32be - uint8 - uint8be - wide - xor Rules are generally composed of two sections: strings definition and condition. The strings definition section can be omitted if the rule doesn't rely on any string, but the condition section is always required. The strings definition section is where the strings that will be part of the rule are defined. Each string has an identifier consisting of a $ character followed by a sequence of alphanumeric characters and underscores, these identifiers can be used in the condition section to refer to the corresponding string. Strings can be defined in text or hexadecimal form, as shown in the following example: .. code-block:: yara rule ExampleRule { strings: $my_text_string = "text here" $my_hex_string = { E2 34 A1 C8 23 FB } condition: $my_text_string or $my_hex_string } Text strings are enclosed in double quotes just like in the C language. Hex strings are enclosed by curly brackets, and they are composed by a sequence of hexadecimal numbers that can appear contiguously or separated by spaces. Decimal numbers are not allowed in hex strings. The condition section is where the logic of the rule resides. This section must contain a boolean expression telling under which circumstances a file or process satisfies the rule or not. Generally, the condition will refer to previously defined strings by using their identifiers. In this context the string identifier acts as a boolean variable which evaluate to true if the string was found in the file or process memory, or false if otherwise. Comments ======== You can add comments to your YARA rules just as if it was a C source file, both single-line and multi-line C-style comments are supported. .. code-block:: yara /* This is a multi-line comment ... */ rule CommentExample // ... and this is single-line comment { condition: false // just a dummy rule, don't do this } Strings ======= There are three types of strings in YARA: hexadecimal strings, text strings and regular expressions. Hexadecimal strings are used for defining raw sequences of bytes, while text strings and regular expressions are useful for defining portions of legible text. However text strings and regular expressions can be also used for representing raw bytes by mean of escape sequences as will be shown below. Hexadecimal strings ------------------- Hexadecimal strings allow three special constructions that make them more flexible: wild-cards, jumps, and alternatives. Wild-cards are just placeholders that you can put into the string indicating that some bytes are unknown and they should match anything. The placeholder character is the question mark (?). Here you have an example of a hexadecimal string with wild-cards: .. code-block:: yara rule WildcardExample { strings: $hex_string = { E2 34 ?? C8 A? FB } condition: $hex_string } As shown in the example the wild-cards are nibble-wise, which means that you can define just one nibble of the byte and leave the other unknown. Wild-cards are useful when defining strings whose content can vary but you know the length of the variable chunks, however, this is not always the case. In some circumstances you may need to define strings with chunks of variable content and length. In those situations you can use jumps instead of wild-cards: .. code-block:: yara rule JumpExample { strings: $hex_string = { F4 23 [4-6] 62 B4 } condition: $hex_string } In the example above we have a pair of numbers enclosed in square brackets and separated by a hyphen, that's a jump. This jump is indicating that any arbitrary sequence from 4 to 6 bytes can occupy the position of the jump. Any of the following strings will match the pattern:: F4 23 01 02 03 04 62 B4 F4 23 00 00 00 00 00 62 B4 F4 23 15 82 A3 04 45 22 62 B4 Any jump [X-Y] must meet the condition 0 <= X <= Y. In previous versions of YARA both X and Y must be lower than 256, but starting with YARA 2.0 there is no limit for X and Y. These are valid jumps:: FE 39 45 [0-8] 89 00 FE 39 45 [23-45] 89 00 FE 39 45 [1000-2000] 89 00 This is invalid:: FE 39 45 [10-7] 89 00 If the lower and higher bounds are equal you can write a single number enclosed in brackets, like this:: FE 39 45 [6] 89 00 The above string is equivalent to both of these:: FE 39 45 [6-6] 89 00 FE 39 45 ?? ?? ?? ?? ?? ?? 89 00 Starting with YARA 2.0 you can also use unbounded jumps:: FE 39 45 [10-] 89 00 FE 39 45 [-] 89 00 The first one means ``[10-infinite]``, the second one means ``[0-infinite]``. There are also situations in which you may want to provide different alternatives for a given fragment of your hex string. In those situations you can use a syntax which resembles a regular expression: .. code-block:: yara rule AlternativesExample1 { strings: $hex_string = { F4 23 ( 62 B4 | 56 ) 45 } condition: $hex_string } This rule will match any file containing ``F42362B445`` or ``F4235645``. But more than two alternatives can be also expressed. In fact, there are no limits to the amount of alternative sequences you can provide, and neither to their lengths. .. code-block:: yara rule AlternativesExample2 { strings: $hex_string = { F4 23 ( 62 B4 | 56 | 45 ?? 67 ) 45 } condition: $hex_string } As can be seen also in the above example, strings containing wild-cards are allowed as part of alternative sequences. Text strings ------------ As shown in previous sections, text strings are generally defined like this: .. code-block:: yara rule TextExample { strings: $text_string = "foobar" condition: $text_string } This is the simplest case: an ASCII-encoded, case-sensitive string. However, text strings can be accompanied by some useful modifiers that alter the way in which the string will be interpreted. Those modifiers are appended at the end of the string definition separated by spaces, as will be discussed below. Text strings can also contain the following subset of the escape sequences available in the C language: .. list-table:: :widths: 3 10 * - ``\"`` - Double quote * - ``\\`` - Backslash * - ``\r`` - Carriage return * - ``\t`` - Horizontal tab * - ``\n`` - New line * - ``\xdd`` - Any byte in hexadecimal notation In all versions of YARA before 4.1.0 text strings accepted any kind of unicode characters, regardless of their encoding. Those characters were interpreted by YARA as raw bytes, and therefore the final string was actually determined by the encoding format used by your text editor. This never meant to be a feature, the original intention always was that YARA strings should be ASCII-only and YARA 4.1.0 started to raise warnings about non-ASCII characters in strings. This limitation does not apply to strings in the metadata section or comments. See more details [here](https://github.com/VirusTotal/yara/wiki/Unicode-characters-in-YARA) Case-insensitive strings ^^^^^^^^^^^^^^^^^^^^^^^^ Text strings in YARA are case-sensitive by default, however you can turn your string into case-insensitive mode by appending the modifier nocase at the end of the string definition, in the same line: .. code-block:: yara rule CaseInsensitiveTextExample { strings: $text_string = "foobar" nocase condition: $text_string } With the ``nocase`` modifier the string *foobar* will match *Foobar*, *FOOBAR*, and *fOoBaR*. This modifier can be used in conjunction with any modifier, except ``base64`` and ``base64wide``. Wide-character strings ^^^^^^^^^^^^^^^^^^^^^^ The ``wide`` modifier can be used to search for strings encoded with two bytes per character, something typical in many executable binaries. For example, if the string "Borland" appears encoded as two bytes per character (i.e. ``B\x00o\x00r\x00l\x00a\x00n\x00d\x00``), then the following rule will match: .. code-block:: yara rule WideCharTextExample1 { strings: $wide_string = "Borland" wide condition: $wide_string } However, keep in mind that this modifier just interleaves the ASCII codes of the characters in the string with zeroes, it does not support truly UTF-16 strings containing non-English characters. If you want to search for strings in both ASCII and wide form, you can use the ``ascii`` modifier in conjunction with ``wide`` , no matter the order in which they appear. .. code-block:: yara rule WideCharTextExample2 { strings: $wide_and_ascii_string = "Borland" wide ascii condition: $wide_and_ascii_string } The ``ascii`` modifier can appear alone, without an accompanying ``wide`` modifier, but it's not necessary to write it because in absence of ``wide`` the string is assumed to be ASCII by default. XOR strings ^^^^^^^^^^^ The ``xor`` modifier can be used to search for strings with a single byte XOR applied to them. The following rule will search for every single byte XOR applied to the string "This program cannot" (including the plaintext string): .. code-block:: yara rule XorExample1 { strings: $xor_string = "This program cannot" xor condition: $xor_string } The above rule is logically equivalent to: .. code-block:: yara rule XorExample2 { strings: $xor_string_00 = "This program cannot" $xor_string_01 = "Uihr!qsnfs`l!b`oonu" $xor_string_02 = "Vjkq\"rpmepco\"acllmv" // Repeat for every single byte XOR condition: any of them } You can also combine the ``xor`` modifier with ``wide`` and ``ascii`` modifiers. For example, to search for the ``wide`` and ``ascii`` versions of a string after every single byte XOR has been applied you would use: .. code-block:: yara rule XorExample3 { strings: $xor_string = "This program cannot" xor wide ascii condition: $xor_string } The ``xor`` modifier is applied after every other modifier. This means that using the ``xor`` and ``wide`` together results in the XOR applying to the interleaved zero bytes. For example, the following two rules are logically equivalent: .. code-block:: yara rule XorExample4 { strings: $xor_string = "This program cannot" xor wide condition: $xor_string } rule XorExample4 { strings: $xor_string_00 = "T\x00h\x00i\x00s\x00 \x00p\x00r\x00o\x00g\x00r\x00a\x00m\x00 \x00c\x00a\x00n\x00n\x00o\x00t\x00" $xor_string_01 = "U\x01i\x01h\x01r\x01!\x01q\x01s\x01n\x01f\x01s\x01`\x01l\x01!\x01b\x01`\x01o\x01o\x01n\x01u\x01" $xor_string_02 = "V\x02j\x02k\x02q\x02\"\x02r\x02p\x02m\x02e\x02p\x02c\x02o\x02\"\x02a\x02c\x02l\x02l\x02m\x02v\x02" // Repeat for every single byte XOR operation. condition: any of them } Since YARA 3.11, if you want more control over the range of bytes used with the ``xor`` modifier use: .. code-block:: yara rule XorExample5 { strings: $xor_string = "This program cannot" xor(0x01-0xff) condition: $xor_string } The above example will apply the bytes from 0x01 to 0xff, inclusively, to the string when searching. The general syntax is ``xor(minimum-maximum)``. Base64 strings ^^^^^^^^^^^^^^ The ``base64`` modifier can be used to search for strings that have been base64 encoded. A good explanation of the technique is at: https://www.leeholmes.com/searching-for-content-in-base-64-strings/ The following rule will search for the three base64 permutations of the string "This program cannot": .. code-block:: yara rule Base64Example1 { strings: $a = "This program cannot" base64 condition: $a } This will cause YARA to search for these three permutations: | VGhpcyBwcm9ncmFtIGNhbm5vd | RoaXMgcHJvZ3JhbSBjYW5ub3 | UaGlzIHByb2dyYW0gY2Fubm90 The ``base64wide`` modifier works just like the ``base64`` modifier but the results of the ``base64`` modifier are converted to wide. The interaction between ``base64`` (or ``base64wide``) and ``wide`` and ``ascii`` is as you might expect. ``wide`` and ``ascii`` are applied to the string first, and then the ``base64`` and ``base64wide`` modifiers are applied. At no point is the plaintext of the ``ascii`` or ``wide`` versions of the strings included in the search. If you want to also include those you can put them in a secondary string. The ``base64`` and ``base64wide`` modifiers also support a custom alphabet. For example: .. code-block:: yara rule Base64Example2 { strings: $a = "This program cannot" base64("!@#$%^&*(){}[].,|ABCDEFGHIJ\x09LMNOPQRSTUVWXYZabcdefghijklmnopqrstu") condition: $a } The alphabet must be 64 bytes long. The ``base64`` and ``base64wide`` modifiers are only supported with text strings. Using these modifiers with a hexadecimal string or a regular expression will cause a compiler error. Also, the ``xor``, ``fullword``, and ``nocase`` modifiers used in combination with ``base64`` or ``base64wide`` will cause a compiler error. Because of the way that YARA strips the leading and trailing characters after base64 encoding, one of the base64 encodings of "Dhis program cannow" and "This program cannot" are identical. Similarly, using the ``base64`` keyword on single ASCII characters is not recommended. For example, "a" with the ``base64`` keyword matches "\`", "b", "c", "!", "\\xA1", or "\\xE1" after base64 encoding, and will not match where the base64 encoding matches the ``[GWm2][EFGH]`` regular expression. Searching for full words ^^^^^^^^^^^^^^^^^^^^^^^^ Another modifier that can be applied to text strings is ``fullword``. This modifier guarantees that the string will match only if it appears in the file delimited by non-alphanumeric characters. For example the string *domain*, if defined as ``fullword``, doesn't match *www.mydomain.com* but it matches *www.my-domain.com* and *www.domain.com*. Regular expressions ------------------- Regular expressions are one of the most powerful features of YARA. They are defined in the same way as text strings, but enclosed in forward slashes instead of double-quotes, like in the Perl programming language. .. code-block:: yara rule RegExpExample1 { strings: $re1 = /md5: [0-9a-fA-F]{32}/ $re2 = /state: (on|off)/ condition: $re1 and $re2 } Regular expressions can be also followed by ``nocase``, ``ascii``, ``wide``, and ``fullword`` modifiers just like in text strings. The semantics of these modifiers are the same in both cases. In previous versions of YARA, external libraries like PCRE and RE2 were used to perform regular expression matching, but starting with version 2.0 YARA uses its own regular expression engine. This new engine implements most features found in PCRE, except a few of them like capture groups, POSIX character classes and backreferences. YARA’s regular expressions recognise the following metacharacters: .. list-table:: :widths: 3 10 * - ``\`` - Quote the next metacharacter * - ``^`` - Match the beginning of the file * - ``$`` - Match the end of the file * - ``|`` - Alternation * - ``()`` - Grouping * - ``[]`` - Bracketed character class The following quantifiers are recognised as well: .. list-table:: :widths: 3 10 * - ``*`` - Match 0 or more times * - ``+`` - Match 1 or more times * - ``?`` - Match 0 or 1 times * - ``{n}`` - Match exactly n times * - ``{n,}`` - Match at least n times * - ``{,m}`` - Match at most m times * - ``{n,m}`` - Match n to m times All these quantifiers have a non-greedy variant, followed by a question mark (?): .. list-table:: :widths: 3 10 * - ``*?`` - Match 0 or more times, non-greedy * - ``+?`` - Match 1 or more times, non-greedy * - ``??`` - Match 0 or 1 times, non-greedy * - ``{n}?`` - Match exactly n times, non-greedy * - ``{n,}?`` - Match at least n times, non-greedy * - ``{,m}?`` - Match at most m times, non-greedy * - ``{n,m}?`` - Match n to m times, non-greedy The following escape sequences are recognised: .. list-table:: :widths: 3 10 * - ``\t`` - Tab (HT, TAB) * - ``\n`` - New line (LF, NL) * - ``\r`` - Return (CR) * - ``\f`` - Form feed (FF) * - ``\a`` - Alarm bell * - ``\xNN`` - Character whose ordinal number is the given hexadecimal number These are the recognised character classes: .. list-table:: :widths: 3 10 * - ``\w`` - Match a *word* character (alphanumeric plus “_”) * - ``\W`` - Match a *non-word* character * - ``\s`` - Match a whitespace character * - ``\S`` - Match a non-whitespace character * - ``\d`` - Match a decimal digit character * - ``\D`` - Match a non-digit character Starting with version 3.3.0 these zero-width assertions are also recognized: .. list-table:: :widths: 3 10 * - ``\b`` - Match a word boundary * - ``\B`` - Match except at a word boundary Private strings --------------- All strings in YARA can be marked as ``private`` which means they will never be included in the output of YARA. They are treated as normal strings everywhere else, so you can still use them as you wish in the condition, but they will never be shown with the ``-s`` flag or seen in the YARA callback if you're using the C API. .. code-block:: yara rule PrivateStringExample { strings: $text_string = "foobar" private condition: $text_string } String Modifier Summary ----------------------- The following string modifiers are processed in the following order, but are only applicable to the string types listed. .. list-table:: Text string modifiers :widths: 3 5 10 10 :header-rows: 1 * - Keyword - String Types - Summary - Restrictions * - ``nocase`` - Text, Regex - Ignore case - Cannot use with ``xor``, ``base64``, or ``base64wide`` * - ``wide`` - Text, Regex - Emulate UTF16 by interleaving null (0x00) characters - None * - ``ascii`` - Text, Regex - Also match ASCII characters, only required if ``wide`` is used - None * - ``xor`` - Text - XOR text string with single byte keys - Cannot use with ``nocase``, ``base64``, or ``base64wide`` * - ``base64`` - Text - Convert to 3 base64 encoded strings - Cannot use with ``nocase``, ``xor``, or ``fullword`` * - ``base64wide`` - Text - Convert to 3 base64 encoded strings, then interleaving null characters like ``wide`` - Cannot use with ``nocase``, ``xor``, or ``fullword`` * - ``fullword`` - Text, Regex - Match is not preceded or followed by an alphanumeric character - Cannot use with ``base64`` or ``base64wide`` * - ``private`` - Hex, Text, Regex - Match never included in output - None Conditions ========== Conditions are nothing more than Boolean expressions as those that can be found in all programming languages, for example in an *if* statement. They can contain the typical Boolean operators ``and``, ``or``, and ``not``, and relational operators ``>=``, ``<=``, ``<``, ``>``, ``==`` and ``!=``. Also, the arithmetic operators (``+``, ``-``, ``*``, ``\``, ``%``) and bitwise operators (``&``, ``|``, ``<<``, ``>>``, ``~``, ``^``) can be used on numerical expressions. Integers are always 64-bits long, even the results of functions like `uint8`, `uint16` and `uint32` are promoted to 64-bits. This is something you must take into account, specially while using bitwise operators (for example, ~0x01 is not 0xFE but 0xFFFFFFFFFFFFFFFE). The following table lists the precedence and associativity of all operators. The table is sorted in descending precedence order, which means that operators listed on a higher row in the list are grouped prior operators listed in rows further below it. Operators within the same row have the same precedence, if they appear together in a expression the associativity determines how they are grouped. ========== =========== ========================================= ============= Precedence Operator Description Associativity ========== =========== ========================================= ============= 1 [] Array subscripting Left-to-right . Structure member access ---------- ----------- ----------------------------------------- ------------- 2 `-` Unary minus Right-to-left `~` Bitwise not ---------- ----------- ----------------------------------------- ------------- 3 `*` Multiplication Left-to-right \\ Division % Remainder ---------- ----------- ----------------------------------------- ------------- 4 `+` Addition Left-to-right `-` Subtraction ---------- ----------- ----------------------------------------- ------------- 5 `<<` Bitwise left shift Left-to-right `>>` Bitwise right shift ---------- ----------- ----------------------------------------- ------------- 6 & Bitwise AND Left-to-right ---------- ----------- ----------------------------------------- ------------- 7 ^ Bitwise XOR Left-to-right ---------- ----------- ----------------------------------------- ------------- 8 `|` Bitwise OR Left-to-right ---------- ----------- ----------------------------------------- ------------- 9 < Less than Left-to-right <= Less than or equal to > Greater than >= Greater than or equal to ---------- ----------- ----------------------------------------- ------------- 10 == Equal to Left-to-right != Not equal to contains String contains substring icontains Like contains but case-insensitive startswith String starts with substring istartswith Like startswith but case-insensitive endswith String ends with substring iendswith Like endswith but case-insensitive matches String matches regular expression ---------- ----------- ----------------------------------------- ------------- 11 not Logical NOT Right-to-left ---------- ----------- ----------------------------------------- ------------- 12 and Logical AND Left-to-right ---------- ----------- ----------------------------------------- ------------- 13 or Logical OR Left-to-right ========== =========== ========================================= ============= String identifiers can be also used within a condition, acting as Boolean variables whose value depends on the presence or not of the associated string in the file. .. code-block:: yara rule Example { strings: $a = "text1" $b = "text2" $c = "text3" $d = "text4" condition: ($a or $b) and ($c or $d) } Counting strings ---------------- Sometimes we need to know not only if a certain string is present or not, but how many times the string appears in the file or process memory. The number of occurrences of each string is represented by a variable whose name is the string identifier but with a # character in place of the $ character. For example: .. code-block:: yara rule CountExample { strings: $a = "dummy1" $b = "dummy2" condition: #a == 6 and #b > 10 } This rule matches any file or process containing the string $a exactly six times, and more than ten occurrences of string $b. .. _string-offsets: String offsets or virtual addresses ----------------------------------- In the majority of cases, when a string identifier is used in a condition, we are willing to know if the associated string is anywhere within the file or process memory, but sometimes we need to know if the string is at some specific offset on the file or at some virtual address within the process address space. In such situations the operator ``at`` is what we need. This operator is used as shown in the following example: .. code-block:: yara rule AtExample { strings: $a = "dummy1" $b = "dummy2" condition: $a at 100 and $b at 200 } The expression ``$a at 100`` in the above example is true only if string $a is found at offset 100 within the file (or at virtual address 100 if applied to a running process). The string $b should appear at offset 200. Please note that both offsets are decimal, however hexadecimal numbers can be written by adding the prefix 0x before the number as in the C language, which comes very handy when writing virtual addresses. Also note the higher precedence of the operator ``at`` over the ``and``. While the ``at`` operator allows to search for a string at some fixed offset in the file or virtual address in a process memory space, the ``in`` operator allows to search for the string within a range of offsets or addresses. .. code-block:: yara rule InExample { strings: $a = "dummy1" $b = "dummy2" condition: $a in (0..100) and $b in (100..filesize) } In the example above the string $a must be found at an offset between 0 and 100, while string $b must be at an offset between 100 and the end of the file. Again, numbers are decimal by default. You can also get the offset or virtual address of the i-th occurrence of string $a by using @a[i]. The indexes are one-based, so the first occurrence would be @a[1] the second one @a[2] and so on. If you provide an index greater then the number of occurrences of the string, the result will be a NaN (Not A Number) value. Match length ------------ For many regular expressions and hex strings containing jumps, the length of the match is variable. If you have the regular expression /fo*/ the strings "fo", "foo" and "fooo" can be matches, all of them with a different length. You can use the length of the matches as part of your condition by using the character ! in front of the string identifier, in a similar way you use the @ character for the offset. !a[1] is the length for the first match of $a, !a[2] is the length for the second match, and so on. !a is a abbreviated form of !a[1]. File size --------- String identifiers are not the only variables that can appear in a condition (in fact, rules can be defined without any string definition as will be shown below), there are other special variables that can be used as well. One of these special variables is ``filesize``, which holds, as its name indicates, the size of the file being scanned. The size is expressed in bytes. .. code-block:: yara rule FileSizeExample { condition: filesize > 200KB } The previous example also demonstrates the use of the ``KB`` postfix. This postfix, when attached to a numerical constant, automatically multiplies the value of the constant by 1024. The ``MB`` postfix can be used to multiply the value by 2^20. Both postfixes can be used only with decimal constants. The use of ``filesize`` only makes sense when the rule is applied to a file. If the rule is applied to a running process it won’t ever match because ``filesize`` doesn’t make sense in this context. Executable entry point ---------------------- Another special variable than can be used in a rule is ``entrypoint``. If the file is a Portable Executable (PE) or Executable and Linkable Format (ELF), this variable holds the raw offset of the executable’s entry point in case we are scanning a file. If we are scanning a running process, the entrypoint will hold the virtual address of the main executable’s entry point. A typical use of this variable is to look for some pattern at the entry point to detect packers or simple file infectors. .. code-block:: yara rule EntryPointExample1 { strings: $a = { E8 00 00 00 00 } condition: $a at entrypoint } rule EntryPointExample2 { strings: $a = { 9C 50 66 A1 ?? ?? ?? 00 66 A9 ?? ?? 58 0F 85 } condition: $a in (entrypoint..entrypoint + 10) } The presence of the ``entrypoint`` variable in a rule implies that only PE or ELF files can satisfy that rule. If the file is not a PE or ELF, any rule using this variable evaluates to false. .. warning:: The ``entrypoint`` variable is deprecated, you should use the equivalent ``pe.entry_point`` from the :ref:`pe-module` instead. Starting with YARA 3.0 you'll get a warning if you use ``entrypoint`` and it will be completely removed in future versions. Accessing data at a given position ---------------------------------- There are many situations in which you may want to write conditions that depend on data stored at a certain file offset or virtual memory address, depending on if we are scanning a file or a running process. In those situations you can use one of the following functions to read data from the file at the given offset:: int8() int16() int32() uint8() uint16() uint32() int8be() int16be() int32be() uint8be() uint16be() uint32be() The ``intXX`` functions read 8, 16, and 32 bits signed integers from , while functions ``uintXX`` read unsigned integers. Both 16 and 32 bit integers are considered to be little-endian. If you want to read a big-endian integer use the corresponding function ending in ``be``. The parameter can be any expression returning an unsigned integer, including the return value of one the ``uintXX`` functions itself. As an example let's see a rule to distinguish PE files: .. code-block:: yara rule IsPE { condition: // MZ signature at offset 0 and ... uint16(0) == 0x5A4D and // ... PE signature at offset stored in MZ header at 0x3C uint32(uint32(0x3C)) == 0x00004550 } Sets of strings --------------- There are circumstances in which it is necessary to express that the file should contain a certain number strings from a given set. None of the strings in the set are required to be present, but at least some of them should be. In these situations the ``of`` operator can be used. .. code-block:: yara rule OfExample1 { strings: $a = "dummy1" $b = "dummy2" $c = "dummy3" condition: 2 of ($a,$b,$c) } This rule requires that at least two of the strings in the set ($a,$b,$c) must be present in the file, but it does not matter which two. Of course, when using this operator, the number before the ``of`` keyword must be less than or equal to the number of strings in the set. The elements of the set can be explicitly enumerated like in the previous example, or can be specified by using wild cards. For example: .. code-block:: yara rule OfExample2 { strings: $foo1 = "foo1" $foo2 = "foo2" $foo3 = "foo3" condition: 2 of ($foo*) // equivalent to 2 of ($foo1,$foo2,$foo3) } rule OfExample3 { strings: $foo1 = "foo1" $foo2 = "foo2" $bar1 = "bar1" $bar2 = "bar2" condition: 3 of ($foo*,$bar1,$bar2) } You can even use ``($*)`` to refer to all the strings in your rule, or write the equivalent keyword ``them`` for more legibility. .. code-block:: yara rule OfExample4 { strings: $a = "dummy1" $b = "dummy2" $c = "dummy3" condition: 1 of them // equivalent to 1 of ($*) } In all the examples above, the number of strings have been specified by a numeric constant, but any expression returning a numeric value can be used. The keywords ``any`` and ``all`` can be used as well. .. code-block:: yara all of them // all strings in the rule any of them // any string in the rule all of ($a*) // all strings whose identifier starts by $a any of ($a,$b,$c) // any of $a, $b or $c 1 of ($*) // same that "any of them" Applying the same condition to many strings ------------------------------------------- There is another operator very similar to ``of`` but even more powerful, the ``for..of`` operator. The syntax is: .. code-block:: yara for expression of string_set : ( boolean_expression ) And its meaning is: from those strings in ``string_set`` at least ``expression`` of them must satisfy ``boolean_expression``. In other words: ``boolean_expression`` is evaluated for every string in ``string_set`` and there must be at least ``expression`` of them returning True. Of course, ``boolean_expression`` can be any boolean expression accepted in the condition section of a rule, except for one important detail: here you can (and should) use a dollar sign ($) as a place-holder for the string being evaluated. Take a look at the following expression: .. code-block:: yara for any of ($a,$b,$c) : ( $ at pe.entry_point ) The $ symbol in the boolean expression is not tied to any particular string, it will be $a, and then $b, and then $c in the three successive evaluations of the expression. Maybe you already realised that the ``of`` operator is a special case of ``for..of``. The following expressions are the same: .. code-block:: yara any of ($a,$b,$c) for any of ($a,$b,$c) : ( $ ) You can also employ the symbols #, @, and ! to make reference to the number of occurrences, the first offset, and the length of each string respectively. .. code-block:: yara for all of them : ( # > 3 ) for all of ($a*) : ( @ > @b ) Using anonymous strings with ``of`` and ``for..of`` --------------------------------------------------- When using the ``of`` and ``for..of`` operators followed by ``them``, the identifier assigned to each string of the rule is usually superfluous. As we are not referencing any string individually we do not need to provide a unique identifier for each of them. In those situations you can declare anonymous strings with identifiers consisting only of the $ character, as in the following example: .. code-block:: yara rule AnonymousStrings { strings: $ = "dummy1" $ = "dummy2" condition: 1 of them } Iterating over string occurrences --------------------------------- As seen in :ref:`string-offsets`, the offsets or virtual addresses where a given string appears within a file or process address space can be accessed by using the syntax: @a[i], where i is an index indicating which occurrence of the string $a you are referring to. (@a[1], @a[2],...). Sometimes you will need to iterate over some of these offsets and guarantee they satisfy a given condition. For example: .. code-block:: yara rule Occurrences { strings: $a = "dummy1" $b = "dummy2" condition: for all i in (1,2,3) : ( @a[i] + 10 == @b[i] ) } The previous rule says that the first three occurrences of $b should be 10 bytes away from the first three occurrences of $a. The same condition could be written also as: .. code-block:: yara for all i in (1..3) : ( @a[i] + 10 == @b[i] ) Notice that we’re using a range (1..3) instead of enumerating the index values (1,2,3). Of course, we’re not forced to use constants to specify range boundaries, we can use expressions as well like in the following example: .. code-block:: yara for all i in (1..#a) : ( @a[i] < 100 ) In this case we’re iterating over every occurrence of $a (remember that #a represents the number of occurrences of $a). This rule is specifying that every occurrence of $a should be within the first 100 bytes of the file. In case you want to express that only some occurrences of the string should satisfy your condition, the same logic seen in the ``for..of`` operator applies here: .. code-block:: yara for any i in (1..#a) : ( @a[i] < 100 ) for 2 i in (1..#a) : ( @a[i] < 100 ) In summary, the syntax of this operator is: .. code-block:: yara for expression identifier in indexes : ( boolean_expression ) Iterators --------- In YARA 4.0 the ``for..of`` operator was improved and now it can be used to iterate not only over integer enumerations and ranges (e.g: 1,2,3,4 and 1..4), but also over any kind of iterable data type, like arrays and dictionaries defined by YARA modules. For example, the following expression is valid in YARA 4.0: .. code-block:: yara for any section in pe.sections : ( section.name == ".text" ) This is equivalent to: .. code-block:: yara for any i in (0..pe.number_of_sections-1) : ( pe.sections[i].name == ".text" ) The new syntax is more natural and easy to understand, and is the recommended way of expressing this type of conditions in newer versions of YARA. While iterating dictionaries you must provide two variable names that will hold the key and value for each entry in the dictionary, for example: .. code-block:: yara for any k,v in some_dict : ( k == "foo" and v == "bar" ) In general the ``for..of`` operator has the form: .. code-block:: yara for in : ( ) Where `` is either `any`, `all` or an expression that evaluates to the number of items in the iterator that must satisfy the condition, `` is a comma-separated list of variable names that holds the values for the current item (the number of variables depend on the type of ``) and `` is something that can be iterated. .. _referencing-rules: Referencing other rules ----------------------- When writing the condition for a rule you can also make reference to a previously defined rule in a manner that resembles a function invocation of traditional programming languages. In this way you can create rules that depend on others. Let's see an example: .. code-block:: yara rule Rule1 { strings: $a = "dummy1" condition: $a } rule Rule2 { strings: $a = "dummy2" condition: $a and Rule1 } As can be seen in the example, a file will satisfy Rule2 only if it contains the string "dummy2" and satisfies Rule1. Note that it is strictly necessary to define the rule being invoked before the one that will make the invocation. More about rules ================ There are some aspects of YARA rules that have not been covered yet, but are still very important. These are: global rules, private rules, tags and metadata. Global rules ------------ Global rules give you the possibility of imposing restrictions in all your rules at once. For example, suppose that you want all your rules to ignore files that exceed a certain size limit. You could go rule by rule making the required modifications to their conditions, or just write a global rule like this one: .. code-block:: yara global rule SizeLimit { condition: filesize < 2MB } You can define as many global rules as you want, they will be evaluated before the rest of the rules, which in turn will be evaluated only if all global rules are satisfied. Private rules ------------- Private rules are a very simple concept. They are just rules that are not reported by YARA when they match on a given file. Rules that are not reported at all may seem sterile at first glance, but when mixed with the possibility offered by YARA of referencing one rule from another (see :ref:`referencing-rules`) they become useful. Private rules can serve as building blocks for other rules, and at the same time prevent cluttering YARA's output with irrelevant information. To declare a rule as private just add the keyword ``private`` before the rule declaration. .. code-block:: yara private rule PrivateRuleExample { ... } You can apply both ``private`` and ``global`` modifiers to a rule, resulting in a global rule that does not get reported by YARA but must be satisfied. Rule tags --------- Another useful feature of YARA is the possibility of adding tags to rules. Those tags can be used later to filter YARA's output and show only the rules that you are interested in. You can add as many tags as you want to a rule, they are declared after the rule identifier as shown below: .. code-block:: yara rule TagsExample1 : Foo Bar Baz { ... } rule TagsExample2 : Bar { ... } Tags must follow the same lexical convention of rule identifiers, therefore only alphanumeric characters and underscores are allowed, and the tag cannot start with a digit. They are also case sensitive. When using YARA you can output only those rules which are tagged with the tag or tags that you provide. Metadata -------- Besides the string definition and condition sections, rules can also have a metadata section where you can put additional information about your rule. The metadata section is defined with the keyword ``meta`` and contains identifier/value pairs like in the following example: .. code-block:: yara rule MetadataExample { meta: my_identifier_1 = "Some string data" my_identifier_2 = 24 my_identifier_3 = true strings: $my_text_string = "text here" $my_hex_string = { E2 34 A1 C8 23 FB } condition: $my_text_string or $my_hex_string } As can be seen in the example, metadata identifiers are always followed by an equals sign and the value assigned to them. The assigned values can be strings (valid UTF8 only), integers, or one of the boolean values true or false. Note that identifier/value pairs defined in the metadata section cannot be used in the condition section, their only purpose is to store additional information about the rule. .. _using-modules: Using modules ============= Modules are extensions to YARA's core functionality. Some modules like the :ref:`PE module ` and the :ref:`Cuckoo module ` are officially distributed with YARA and additional ones can be created by third-parties or even yourself as described in :ref:`writing-modules`. The first step to using a module is importing it with the ``import`` statement. These statements must be placed outside any rule definition and followed by the module name enclosed in double-quotes. Like this: .. code-block:: yara import "pe" import "cuckoo" After importing the module you can make use of its features, always using ``.`` as a prefix to any variable or function exported by the module. For example: .. code-block:: yara pe.entry_point == 0x1000 cuckoo.http_request(/someregexp/) .. _undefined-values: Undefined values ================ Modules often leave variables in an undefined state, for example when the variable doesn't make sense in the current context (think of ``pe.entry_point`` while scanning a non-PE file). YARA handles undefined values in a way that allows the rule to keep its meaningfulness. Take a look at this rule: .. code-block:: yara import "pe" rule Test { strings: $a = "some string" condition: $a and pe.entry_point == 0x1000 } If the scanned file is not a PE you wouldn't expect this rule to match the file, even if it contains the string, because **both** conditions (the presence of the string and the right value for the entry point) must be satisfied. However, if the condition is changed to: .. code-block:: yara $a or pe.entry_point == 0x1000 You would expect the rule to match in this case if the file contains the string, even if it isn't a PE file. That's exactly how YARA behaves. The logic is as follows: * If the expression in the condition is undefined, it would be translated to ``false`` and the rule won't match. * Boolean operators ``and`` and ``or`` will treat undefined operands as ``false``, Which means that: * ``undefined and true`` is ``false`` * ``undefined and false`` is ``false`` * ``undefined or true`` is ``true`` * ``undefined or false`` is ``false`` * All the remaining operators, including the ``not`` operator, return undefined if any of their operands is undefined. In the expression above, ``pe.entry_point == 0x1000`` will be undefined for non-PE files, because ``pe.entry_point`` is undefined for those files. This implies that ``$a or pe.entry_point == 0x1000`` will be ``true`` if and only if ``$a`` is ``true``. If the condition is ``pe.entry_point == 0x1000`` alone, it will evaluate to ``false`` for non-PE files, and so will do ``pe.entry_point != 0x1000`` and ``not pe.entry_point == 0x1000``, as non of these expressions make sense for non-PE files. External variables ================== External variables allow you to define rules that depend on values provided from the outside. For example, you can write the following rule: .. code-block:: yara rule ExternalVariableExample1 { condition: ext_var == 10 } In this case ``ext_var`` is an external variable whose value is assigned at run-time (see ``-d`` option of command-line tool, and ``externals`` parameter of ``compile`` and ``match`` methods in yara-python). External variables could be of types: integer, string or boolean; their type depends on the value assigned to them. An integer variable can substitute any integer constant in the condition and boolean variables can occupy the place of boolean expressions. For example: .. code-block:: yara rule ExternalVariableExample2 { condition: bool_ext_var or filesize < int_ext_var } External variables of type string can be used with the operators: ``contains``, ``startswith``, ``endswith`` and their case-insensitive counterparts: ``icontains``, ``istartswith`` and ``iendswith`. They can be used also with the ``matches`` operator, which returns true if the string matches a given regular expression. .. code-block:: yara rule ContainsExample { condition: string_ext_var contains "text" } rule CaseInsensitiveContainsExample { condition: string_ext_var icontains "text" } rule StartsWithExample { condition: string_ext_var startswith "prefix" } rule EndsWithExample { condition: string_ext_var endswith "suffix" } rule MatchesExample { condition: string_ext_var matches /[a-z]+/ } You can use regular expression modifiers along with the ``matches`` operator, for example, if you want the regular expression from the previous example to be case insensitive you can use ``/[a-z]+/i``. Notice the ``i`` following the regular expression in a Perl-like manner. You can also use the ``s`` modifier for single-line mode, in this mode the dot matches all characters including line breaks. Of course both modifiers can be used simultaneously, like in the following example: .. code-block:: yara rule ExternalVariableExample5 { condition: /* case insensitive single-line mode */ string_ext_var matches /[a-z]+/is } Keep in mind that every external variable used in your rules must be defined at run-time, either by using the ``-d`` option of the command-line tool, or by providing the ``externals`` parameter to the appropriate method in ``yara-python``. Including files =============== In order to allow for more flexible organization of your rules files, YARA provides the ``include`` directive. This directive works in a similar way to the *#include* pre-processor directive in C programs, which inserts the content of the specified source file into the current file during compilation. The following example will include the content of *other.yar* into the current file: .. code-block:: yara include "other.yar" The base path when searching for a file in an ``include`` directive will be the directory where the current file resides. For this reason, the file *other.yar* in the previous example should be located in the same directory of the current file. However, you can also specify relative paths like these: .. code-block:: yara include "./includes/other.yar" include "../includes/other.yar" Or use absolute paths: .. code-block:: yara include "/home/plusvic/yara/includes/other.yar" In Windows, both forward and back slashes are accepted, but don’t forget to write the drive letter: .. code-block:: yara include "c:/yara/includes/other.yar" include "c:\\yara\\includes\\other.yar" yara-4.1.3/docs/yarapython.rst000066400000000000000000000365531413423160300163440ustar00rootroot00000000000000********************** Using YARA from Python ********************** YARA can be also used from Python through the ``yara-python`` library. Once the library is built and installed as described in :ref:`compiling-yara` you'll have access to the full potential of YARA from your Python scripts. The first step is importing the YARA library: .. code-block:: python import yara Then you will need to compile your YARA rules before applying them to your data, the rules can be compiled from a file path: .. code-block:: python rules = yara.compile(filepath='/foo/bar/myrules') The default argument is filepath, so you don't need to explicitly specify its name: .. code-block:: python rules = yara.compile('/foo/bar/myrules') You can also compile your rules from a file object: .. code-block:: python fh = open('/foo/bar/myrules') rules = yara.compile(file=fh) fh.close() Or you can compile them directly from a Python string: .. code-block:: python rules = yara.compile(source='rule dummy { condition: true }') If you want to compile a group of files or strings at the same time you can do it by using the ``filepaths`` or ``sources`` named arguments: .. code-block:: python rules = yara.compile(filepaths={ 'namespace1':'/my/path/rules1', 'namespace2':'/my/path/rules2' }) rules = yara.compile(sources={ 'namespace1':'rule dummy { condition: true }', 'namespace2':'rule dummy { condition: false }' }) Notice that both ``filepaths`` and ``sources`` must be dictionaries with keys of string type. The dictionary keys are used as a namespace identifier, allowing to differentiate between rules with the same name in different sources, as occurs in the second example with the *dummy* name. The ``compile`` method also has an optional boolean parameter named ``includes`` which allows you to control whether or not the include directive should be accepted in the source files, for example: .. code-block:: python rules = yara.compile('/foo/bar/my_rules', includes=False) If the source file contains include directives the previous line would raise an exception. If includes are used, a python callback can be set to define a custom source for the imported files (by default they are read from disk). This callback function is set through the ``include_callback`` optional parameter. It receives the following parameters: * ``requested_filename``: file requested with 'include' * ``filename``: file containing the 'include' directive if applicable, else None * ``namespace``: namespace And returns the requested rules sources as a single string. .. code-block:: python import yara import sys if sys.version_info >= (3, 0): import urllib.request as urllib else: import urllib as urllib def mycallback(requested_filename, filename, namespace): if requested_filename == 'req.yara': uf = urllib.urlopen('https://pastebin.com/raw/siZ2sMTM') sources = uf.read() if sys.version_info >= (3, 0): sources = str(sources, 'utf-8') return sources else: raise Exception(filename+": Can't fetch "+requested_filename) rules = yara.compile(source='include "req.yara" rule r{ condition: true }', include_callback=mycallback) If you are using external variables in your rules you must define those external variables either while compiling the rules, or while applying the rules to some file. To define your variables at the moment of compilation you should pass the ``externals`` parameter to the ``compile`` method. For example: .. code-block:: python rules = yara.compile('/foo/bar/my_rules’, externals= {'var1': 'some string’, 'var2': 4, 'var3': True}) The ``externals`` parameter must be a dictionary with the names of the variables as keys and an associated value of either string, integer or boolean type. The ``compile`` method also accepts the optional boolean argument ``error_on_warning``. This arguments tells YARA to raise an exception when a warning is issued during compilation. Such warnings are typically issued when your rules contains some construct that could be slowing down the scanning. The default value for the ``error_on_warning`` argument is False. In all cases ``compile`` returns an instance of the class :py:class:`yara.Rules` Rules. This class has a ``save`` method that can be used to save the compiled rules to a file: .. code-block:: python rules.save('/foo/bar/my_compiled_rules') The compiled rules can be loaded later by using the ``load`` method: .. code-block:: python rules = yara.load('/foo/bar/my_compiled_rules') Starting with YARA 3.4 both ``save`` and ``load`` accept file objects. For example, you can save your rules to a memory buffer with this code: .. code-block:: python import StringIO buff = StringIO.StringIO() rules.save(file=buff) The saved rules can be loaded from the memory buffer: .. code-block:: python buff.seek(0) rule = yara.load(file=buff) The result of ``load`` is also an instance of the class :py:class:`yara.Rules`. Instances of ``Rules`` also have a ``match`` method, which allows you to apply the rules to a file: .. code-block:: python matches = rules.match('/foo/bar/my_file') But you can also apply the rules to a Python string: .. code-block:: python with open('/foo/bar/my_file', 'rb') as f: matches = rules.match(data=f.read()) Or to a running process: .. code-block:: python matches = rules.match(pid=1234) As in the case of ``compile``, the ``match`` method can receive definitions for external variables in the ``externals`` argument. .. code-block:: python matches = rules.match('/foo/bar/my_file', externals= {'var1': 'some other string', 'var2': 100}) External variables defined during compile-time don’t need to be defined again in subsequent calls to the ``match`` method. However you can redefine any variable as needed, or provide additional definitions that weren’t provided during compilation. In some situations involving a very large set of rules or huge files the ``match`` method can take too much time to run. In those situations you may find useful the ``timeout`` argument: .. code-block:: python matches = rules.match('/foo/bar/my_huge_file', timeout=60) If the ``match`` function does not finish before the specified number of seconds elapsed, a ``TimeoutError`` exception is raised. You can also specify a callback function when invoking the ``match`` method. By default, the provided function will be called for every rule, no matter if matching or not. You can choose when your callback function is called by setting the ``which_callbacks`` parameter to one of ``yara.CALLBACK_MATCHES``, ``yara.CALLBACK_NON_MATCHES`` or ``yara.CALLBACK_ALL``. The default is to use ``yara.CALLBACK_ALL``. Your callback function should expect a single parameter of dictionary type, and should return ``CALLBACK_CONTINUE`` to proceed to the next rule or ``CALLBACK_ABORT`` to stop applying rules to your data. Here is an example: .. code-block:: python import yara def mycallback(data): print(data) return yara.CALLBACK_CONTINUE matches = rules.match('/foo/bar/my_file', callback=mycallback, which_callbacks=yara.CALLBACK_MATCHES) The passed dictionary will be something like this: .. code-block:: python { 'tags': ['foo', 'bar'], 'matches': True, 'namespace': 'default', 'rule': 'my_rule', 'meta': {}, 'strings': [(81L, '$a', 'abc'), (141L, '$b', 'def')] } The *matches* field indicates if the rule matches the data or not. The *strings* fields is a list of matching strings, with vectors of the form:: (, , ) The ``match`` method returns a list of instances of the class :py:class:`yara.Match`. Instances of this class have the same attributes as the dictionary passed to the callback function. You can also specify a module callback function when invoking the ``match`` method. The provided function will be called for every imported module that scanned a file. Your callback function should expect a single parameter of dictionary type, and should return ``CALLBACK_CONTINUE`` to proceed to the next rule or ``CALLBACK_ABORT`` to stop applying rules to your data. Here is an example: .. code-block:: python import yara def modules_callback(data): print(data) return yara.CALLBACK_CONTINUE matches = rules.match('/foo/bar/my_file', modules_callback=modules_callback) The passed dictionary will contain the information from the module. You can also specify a warning callback function when invoking the ``match`` method. The provided function will be called for every runtime warning. Your callback function should expect two parameters. The first is an integer which contains the type of warning and the second is a string with the warning message. Your callback should return ``CALLBACK_CONTINUE`` to proceed with the scan or ``CALLBACK_ABORT`` to stop. Possible values for the type are:: CALLBACK_TOO_MANY_MATCHES Here is an example: .. code-block:: python import yara def warnings_callback(warning_type, message): if warning_type == yara.CALLBACK_TOO_MANY_MATCHES: print(message) return yara.CALLBACK_CONTINUE matches = rules.match('/foo/bar/my_file', warnings_callback=warnings_callback) If you do not use a warning callback a warning message will be sent to the normal python warning system for you and scanning will continue. You may also find that the default sizes for the stack for the matching engine in yara or the default size for the maximum number of strings per rule is too low. In the C libyara API, you can modify these using the ``YR_CONFIG_STACK_SIZE`` and ``YR_CONFIG_MAX_STRINGS_PER_RULE`` variables via the ``yr_set_configuration`` function in libyara. The command-line tool exposes these as the ``--stack-size`` (``-k``) and ``--max-strings-per-rule`` command-line arguments. In order to set these values via the Python API, you can use ``yara.set_config`` with either or both ``stack_size`` and ``max_strings_per_rule`` provided as kwargs. At the time of this writing, the default stack size was ``16384`` and the default maximum strings per rule was ``10000``. Also, ``yara.set_config`` accepts the `max_match_data` argument for controlling the maximum number of bytes that will be returned for each matching string. This is equivalent to using ``YR_CONFIG_MAX_MATCH_DATA`` with the ``yr_set_configuration`` in the C API. By the default this is set to 512. Here are a few example calls: .. code-block:: python yara.set_config(stack_size=65536) yara.set_config(max_strings_per_rule=50000, stack_size=65536) yara.set_config(max_strings_per_rule=20000) yara.set_config(max_match_data=128) Reference --------- .. py:module:: yara .. py:function:: yara.compile(...) Compile YARA sources. Either *filepath*, *source*, *file*, *filepaths* or *sources* must be provided. The remaining arguments are optional. :param str filepath: Path to the source file. :param str source: String containing the rules code. :param file-object file: Source file as a file object. :param dict filepaths: Dictionary where keys are namespaces and values are paths to source files. :param dict sources: Dictionary where keys are namespaces and values are strings containing rules code. :param dict externals: Dictionary with external variables. Keys are variable names and values are variable values. :param boolean includes: True if include directives are allowed or False otherwise. Default value: *True*. :param boolean error_on_warning: If true warnings are treated as errors, raising an exception. :return: Compiled rules object. :rtype: :py:class:`yara.Rules` :raises yara.SyntaxError: If a syntax error was found. :raises yara.Error: If an error occurred. .. py:function:: yara.load(...) .. versionchanged:: 3.4.0 Load compiled rules from a path or file object. Either *filepath* or *file* must be provided. :param str filepath: Path to a compiled rules file :param file-object file: A file object supporting the ``read`` method. :return: Compiled rules object. :rtype: :py:class:`yara.Rules` :raises: **yara.Error**: If an error occurred while loading the file. .. py:function:: yara.set_config(...) Set the configuration variables accessible through the yr_set_configuration API. Provide either *stack_size*, *max_strings_per_rule*, or *max_match_data*. These kwargs take unsigned integer values as input and will assign the provided value to the yr_set_configuration(...) variables ``YR_CONFIG_STACK_SIZE``, ``YR_CONFIG_MAX_STRINGS_PER_RULE``, and ``YR_CONFIG_MAX_MATCH_DATA`` respectively. :param int stack_size: Stack size to use for ``YR_CONFIG_STACK_SIZE`` :param int max_strings_per_rule: Maximum number of strings to allow per yara rule. Will be mapped to ``YR_CONFIG_MAX_STRINGS_PER_RULE``. :param int max_match_data: Maximum number of bytes to allow per yara match. Will be mapped to ``YR_CONFIG_MAX_MATCH_DATA``. :return: None :rtype: **NoneType** :raises: **yara.Error**: If an error occurred. .. py:class:: Rules Instances of this class are returned by :py:func:`yara.compile` and represents a set of compiled rules. .. py:method:: match(filepath, pid, data, externals=None, callback=None, fast=False, timeout=None, modules_data=None, modules_callback=None, which_callbacks=CALLBACK_ALL) Scan a file, process memory or data string. Either *filepath*, *pid* or *data* must be provided. The remaining arguments are optional. :param str filepath: Path to the file to be scanned. :param int pid: Process id to be scanned. :param str data: Data to be scanned. :param dict externals: Dictionary with external variables. Keys are variable names and values are variable values. :param function callback: Callback function invoked for each rule. :param bool fast: If true performs a fast mode scan. :param int timeout: Aborts the scanning when the number of specified seconds have elapsed. :param dict modules_data: Dictionary with additional data to modules. Keys are module names and values are *bytes* objects containing the additional data. :param function modules_callback: Callback function invoked for each module. :param int which_callbacks: An integer that indicates in which cases the callback function must be called. Possible values are ``yara.CALLBACK_ALL``, ``yara.CALLBACK_MATCHES`` and ``yara.CALLBACK_NON_MATCHES``. :raises yara.TimeoutError: If the timeout was reached. :raises yara.Error: If an error occurred during the scan. .. py:method:: save(...) .. versionchanged:: 3.4.0 Save compiled rules to a file. Either *filepath* or *file* must be provided. :param str filepath: Path to the file. :param file-object file: A file object supporting the ``write`` method. :raises: **yara.Error**: If an error occurred while saving the file. .. py:class:: Match Objects returned by :py:meth:`yara.Rules.match`, representing a match. .. py:attribute:: rule Name of the matching rule. .. py:attribute:: namespace Namespace associated to the matching rule. .. py:attribute:: tags Array of strings containing the tags associated to the matching rule. .. py:attribute:: meta Dictionary containing metadata associated to the matching rule. .. py:attribute:: strings List of tuples containing information about the matching strings. Each tuple has the form: `(, , )`. yara-4.1.3/extra/000077500000000000000000000000001413423160300135735ustar00rootroot00000000000000yara-4.1.3/extra/TextMate-bundle.zip000066400000000000000000000073061413423160300173270ustar00rootroot00000000000000PK cv>YARA.tmbundle/UX 녈MMPKcv>YARA.tmbundle/info.plistUX MM]N0E+$J2#U te[`=:swע/=Nf8#t k xևJ"ۚɡjTnmD-PUj#}9<.T NPգ;^FAOi >i ɍ';WN\ɨ\“"'H3JBJ(!升/V,PK4,PK dv>YARA.tmbundle/Snippets/UX MMPKdv>%YARA.tmbundle/Snippets/rule.tmSnippetUX MMKo0p- <$p.q"izoF˖_ U˲X,/6,='?}IB\8ٮ㍏ XUU.4IyNaw9@۶ⲳp1֐Jc1*>BB3 F2U=N\roRկ9i!s usM)vWĭO^S%{no};*;҇,hp<h6A ߟPKMyPK ev> __MACOSX/UX MMPK ev>__MACOSX/YARA.tmbundle/UX MMPK ev> __MACOSX/YARA.tmbundle/Snippets/UX MMPKdv>0__MACOSX/YARA.tmbundle/Snippets/._rule.tmSnippetUX MMc`cg`b`MLVVP'q;!!A:fFhr~^nbrQ~nbIj^rbQj CŜςs k4+sլ 39dPKnPK bv>YARA.tmbundle/Syntaxes/UX 녈M녈MPK;cv>&YARA.tmbundle/Syntaxes/YARA.tmLanguageUX MBMWio@`*ٛBզR*J ƞnvMg\T$űg̼7;{ָ(ͤxm" giЯzZuv|PL9Ӧx|w",z>!4@~kx|Uޅ1B~@3T^Xa0$EքŦV] aZtdhJ<~b2uFHDцFzcFEo;g}Bۢ=Xğ56q)5XP/؆.TaVL16jZ`#De:4;̽0+"6 laUtKFȼɲ,粘y6$ir)4ۊU;i%#Rr&`9unf4Gv"OwkFQdDy^Ӑ"K#L&Tmnk7].eu(RŮMS끡9%tT$N*'qNp Ʃ+duprbt:uy_Y1sd8s= ecs;{>~bG&;1s!@g eBuݻ򇊿KN?\z^A PlXa{X|gbhwGvs|:WT1M⬨;oEFa_BoyNP}@$fK6Sv{OxH v=#چt9e-!xZ,T*~Wmn[Ρn q&jdOPKgA PK ev> __MACOSX/YARA.tmbundle/Syntaxes/UX MMPK;cv>1__MACOSX/YARA.tmbundle/Syntaxes/._YARA.tmLanguageUX MBMc`cg`b`MLVVP'q'!!A:f2Fhr~^nbrQ~nbIj^rbQj C59y F`̼T ȚPK@;jPKcv>__MACOSX/._YARA.tmbundleUX 녈MMc`cg`b`MLVVP'A @+PK%!(RPK cv> @AYARA.tmbundle/UX녈MMPKcv>4, @<YARA.tmbundle/info.plistUXMMPK dv> @AoYARA.tmbundle/Snippets/UXMMPKdv>My% @YARA.tmbundle/Snippets/rule.tmSnippetUXMMPK ev> @A4__MACOSX/UXMMPK ev> @Ak__MACOSX/YARA.tmbundle/UXMMPK ev> @A__MACOSX/YARA.tmbundle/Snippets/UXMMPKdv>n0 @__MACOSX/YARA.tmbundle/Snippets/._rule.tmSnippetUXMMPK bv> @AYARA.tmbundle/Syntaxes/UX녈M녈MPK;cv>gA & @YARA.tmbundle/Syntaxes/YARA.tmLanguageUXMBMPK ev> @A__MACOSX/YARA.tmbundle/Syntaxes/UXMMPK;cv>@;j1 @__MACOSX/YARA.tmbundle/Syntaxes/._YARA.tmLanguageUXMBMPKcv>%!(R @ __MACOSX/._YARA.tmbundleUX녈MMPK jF yara-4.1.3/extra/UltraEdit-wordfile.txt000066400000000000000000000013111413423160300200360ustar00rootroot00000000000000/L20"YARA rules" YARA_LANG Line Comment = // Block Comment On = /* Block Comment Off = */ Escape Char = \ String Chars = " File Extensions = YAR /Marker Characters = "//" /Delimiters = ~!@%^&*()-+=|\/{}[]<>:;"' , .? /Function String = "rule [a-zA-Z0-9_]*" /Indent Strings = "{" /Unindent Strings = "}" /Open Brace Strings = "{" "(" "[" /Close Brace Strings = "}" ")" "]" /C1"YARA Keywords" and at any all ascii condition contains endswith entrypoint for false filesize fullword global icontains iendswith is istartswith in include int8 int16 int32 meta matches nocase not or of private rule rva section startswith strings them true uint8 uint16 uint32 wide /C4"YARA Strings" " // / yara-4.1.3/extra/codemirror/000077500000000000000000000000001413423160300157405ustar00rootroot00000000000000yara-4.1.3/extra/codemirror/index.html000066400000000000000000000026661413423160300177470ustar00rootroot00000000000000 CodeMirror: YARA mode

YARA mode

MIME type: text/x-yara

yara-4.1.3/extra/codemirror/yara.js000066400000000000000000000055271413423160300172430ustar00rootroot00000000000000/* Language mode for CodeMirror (https://codemirror.net/) */ CodeMirror.defineMode("yara", function(config) { function words(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; return obj; } var keywords = words("all and any ascii at base64 base64wide condition contains endswith entrypoint filesize for " + "fullword global icontains iendswith import in include int16 int32 int8 istartswith matches meta " + "nocase not of or private rule startswith strings them uint16 uint32 " + "uint8 wide xor"); var atoms = {"true": true, "false": true}; var isOperatorChar = /[+\-*&%=<>!?|\/]/; function tokenBase(stream, state) { var ch = stream.next(); if (ch == "#" && state.startOfLine) { stream.skipToEnd(); return "meta"; } if (/[\[\]{}\(\),;\:\.]/.test(ch)) { return null } if (/\d/.test(ch)) { stream.eatWhile(/[\w\.]/); return "number"; } if (ch == "/") { if (stream.eat("/")) { stream.skipToEnd(); return "comment"; } if (stream.eat("*")) { state.tokenize = tokenComment; return tokenComment(stream, state); } } if (ch == '"' || ch == '/') { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } if (isOperatorChar.test(ch)) { stream.eatWhile(isOperatorChar); return "operator"; } stream.eatWhile(/[\w\$_]/); var cur = stream.current(); if (keywords.propertyIsEnumerable(cur)) return "keyword"; if (atoms.propertyIsEnumerable(cur)) return "atom"; return "word"; } function tokenString(quote) { return function(stream, state) { var escaped = false, next, end = false; while ((next = stream.next()) != null) { if (next == quote && !escaped) {end = true; break;} escaped = !escaped && next == "\\"; } if (end || !escaped) state.tokenize = null; return "string"; }; } function tokenComment(stream, state) { var maybeEnd = false, ch; while (ch = stream.next()) { if (ch == "/" && maybeEnd) { state.tokenize = null; break; } maybeEnd = (ch == "*"); } return "comment"; } // Interface return { startState: function(basecolumn) { return {tokenize: null}; }, token: function(stream, state) { if (stream.eatSpace()) return null; var style = (state.tokenize || tokenBase)(stream, state); return style; }, electricChars: "{}" }; }); CodeMirror.defineMIME("text/yara", "yara"); CodeMirror.defineMIME("text/x-yara", "yara"); yara-4.1.3/extra/logo.ai000066400000000000000000001562441413423160300150620ustar00rootroot00000000000000%PDF-1.5 % 1 0 obj <>/OCGs[5 0 R 21 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream application/pdf logo 2015-12-10T09:39:10+01:00 2015-12-10T09:39:10+01:00 2015-12-10T09:33:55+01:00 Adobe Illustrator CS5 256 256 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXm/5kfnF/gvXINL/AER9f9a1S69b6x6N OckkfHj6Un++61rlGTNwmqd32b2P+axmfFw0a5X3eY72NWf/ADkhdXt3FaWnlVp7mdxHDCl5VmZj QAAW+Q/M+TnT9mxEGUstAf0f+PPYdKn1WezSXU7SOxumALW8UxuAtexf04hUewp75kRJPN5rLGAl UDxDvqv0lJPzD86jyd5fGrmz+vVnSAQep6X2wxry4ydOPhkcuTgFuV2dovzOTgvh2vvZBp919csL a748PrESS8K1481DUrQVpXJRNgFxMkeGRHcVfJMHYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXYq7FXzh/zkh/ynFj/wBsyL/qInzC1P1fB7b2b/xeX9c/dFb/AM46 WFrcedLq5lXlLZ2bvb17M7pGW/4FiPpyOnFzX2jyEYAB/FJ7h+YHmS48teT9S1q2jWW4tUQQo9Sv OWVYlLAEEgF69czMkjGNh5Xs/TDPmjjPI/oFvmfzX+aPm7zTp6afqs8TWiSCb04olSrqCASRvtyP fMCWSUuZe50nZeHBLigDfvfU2kXEFt5Zsrm4kWKCCyikmlY0VUSIMzE+AAzPx/SPc+f5/wC8l7yk n/K3fyv/AOpp0z/pJj/rk2plqOjoroQyMAVYdCDuDiqXa95m8veX7eO51vUbfTbeV/TiluZFjVno W4gt3oCcVQWi/mB5I1y+Fho+uWV/elS4t7eZJH4r9o8VPQYqu13z15N0C7Sz1vWrPTrqSMTJBczJ G5jZiocBj0LIR9GKpd/yt38r/wDqadM/6SY/64q2v5uflezBR5p0ypNBW6iA+8nFWR6bqumanard 6beQX1q/2Li2kSaM/J0LLiqnrWu6Nodib/WL2GwslYI1xcOI0DNso5HbfFUp0v8AMnyBqt/Dp+m+ YLC8vrgkQ20M6PI5ALEKoNTsCcVQmpfm9+Wmm6mdLu/MFqt8riNoULSlXJpxYxq6hq9QTiqYa1+Y HkjQ742Gsa5ZWF6FDm3uJkjfi32TxY9DiqA/5W7+V/8A1NOmf9JMf9cVbX82/wAsGYKPNOmVY0Fb qIDfxJNBirJrHULDULZbqwuYru2f7E8DrJGfkyEg4qr4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXzh/ zkh/ynFj/wBsyL/qInzC1P1fB7b2b/xeX9c/dFI/yT1v9FfmHp4ZuMOoB7GX39YVjH/I1UyvFKpB yu28HiaaXfH1fL9lvo7zzoba75Q1bSkFZbm3b0B4yp8cf/DqMzcsbiQ8Voc/hZoz7j9nV8bkEEgi hGxBzXPpb681b/yV17/2w5f+oQ5ssX0j3Pl2o/vJe8vz/wAmwfpBpn/HNtP+MMf/ABEYsXiH/OX3 /KEaN/20x/1DyYpDyz/nFn/ya8P/ADBXP6hipegf85GflN+YHnDzvY6n5d0r69ZQ6ZFbSS/WLaGk q3E7leM0kbfZkU1pTFQ8s/6Fw/Of/qXv+nyx/wCq+KbQWr/kP+bWk2E1/e+XpRawKXlaGa2uGVVF S3CGWR6Ae2K2lH5f/mF5g8ka9DqmlTt6QYfXLEsfRuIq/Ejr0rT7LdVPTFX09/zkjqFtqX5KrqNq 3O1vZrK4gbxjl+NT9xxYh8reS7TzNeeZbS08sBm1y4EsVmEdIm+OF1fi8jIqn0y1DUffiyZvp3/O O35xw6hazSeX+Mcc0bu31yxNFVgSdp8UWz78/Pyd/MbzV+YMuraBpH1zT2tYIhP9YtYvjQHkOMss bbfLFQ85/wChcPzn/wCpe/6fLH/qvim0u138jvzV0LTZdS1LQJUsoAWmlilt7goo3LMsEkjBR3NK DFbQv5Z/mTrvkXzDBf2M7nT3kUalp9f3c8NaMCp2DgfZbqD7VGKl96288VxbxXELcopkWSNvFWFQ fuOLFUxV2KuxV2KuxV2KuxV2KuxV2KuxV84f85If8pxY/wDbMi/6iJ8wtT9Xwe29m/8AF5f1z90W Vfl/+Rmj2lrp3mDW7ySa7VYr1beMiOGMiki82pzbjtXcZLHgFcRLru0O3ZyMseMVHcX1PRPvMX58 +R9KMsNq82qXcdV9OBCkYYdmkk4/eobJy1Mem7iabsHUZKJqI8/1B806ldre6jdXixiFbmaSYQqa hBIxbiDt0rTMJ7nFDhiI86FPrTVv/JXXv/bDl/6hDmyxfSPc+Y6j+8l7y/P/ACbB+kGmf8c20/4w x/8AERixeIf85ff8oRo3/bTH/UPJikPLP+cWf/Jrw/8AMFc/qGKl9C/mn+ePlj8v2js54n1LWpk9 RNOgZU4IejTSHlwDdvhJ9qYoeUv/AM5j35YlPK0QSvwg3jEge5EI/Vimlp/5zF1Eih8rwkH/AJe2 /wCqWK0+e764S5vbi4jiEEc0jyJCv2UDMSFHTZa0xS+mfzIZm/5xX8vFiWP1fTBU77BQAPoGKHkH 5A/+Tf8ALf8Axml/6h5MUl90YsXmX5p/n15Z8g3Q0xoJNV1soJGsYWEaxqwqvrSkNxLdgFJpv4VV eYN/zmNqHI8fK0IWuwN4xNPn6IxTSx/+cw790ZH8qwMjAhlN2xBB2IIMWK0+dcUv0H/LiWSb8vPK 80h5SSaRYO7bCrNbRknbFiyLFXYq7FXYq7FXYq7FXYq7FXYq7FXzh/zkh/ynFj/2zIv+oifMLU/V 8HtvZv8AxeX9c/dFmv5LfmJ5v8yyyWGo2kU1jYxAPqaAxsG6IjKKo5IB6UyeDJImujqu2uzsOACU SRKR+lP/AM3NN8mr5S1HUtbsoXuViZLO4CqtwbhlIiVJB8X2tyOlB0yeeMeGzzcTsjJn8aMcZNXv 3V1fKmYL6A+wb63lufy2uLeFeUs2jPHGo7s1qQB95zZYvpHufLtR/eS95fn3k2D748p/mb5D1by9 YXkGuWUXKCMSQT3EUUsbhBySRHYMGU/2bYsXlH/OV3mDQdT8m6RFp2pWt7Kmohnjt545WC+hIKkI zECpxSHnn/OLP/k14f8AmCuf1DFSwr809Ru9Q/MjzNc3dfW/SVzFxbcqkMpijT/YIgX6MUh6l+RX kX8lNb8sS3nm68tptaM7q1lc3rWfpRLx4FVSSFn5deW47dsUF6V/yqb/AJxl/k07/uLz/wDZVii3 yPrcNrBrN/DaU+qxXMyW/FuQ9NZCEoxJr8PeuLJ9JfmN/wCsreX/APjBpv6sUPIfyB/8m/5b/wCM 0v8A1DyYpL7oxYvz08+ahd6h51168u2LXE1/clye1JWAX5KBQYsg9l/JLyB+RuseT0vvNN5bXOuS SSfWbS5vmszAoYrGqoksJYMo5cjXr7YoJehf8qm/5xl/k07/ALi8/wD2VYot8dYsn6Cfll/5Lbyn /wBsbT/+oWPFiyXFXYq7FXYq7FXYq7FXYq7FXYq7FXzx+f8AYXWo/mRpFhaLzubuwt4YU8XkuZ1X 8TmFqPqD2Xs/kENLOR5CRP8AsQ9u8oeVtP8ALGgW2kWSjjCKzS0o0srAc5G92P3DbMrHDhFPLazV Sz5DOXX7B3PnH84vPsvmjzI9vbP/ALh9NZobNR0kYGjzHx5EfD/k/M5g5cnEfJ7XsbQDBis/XLn+ pgGVu4fa/l//AI4Om/8AMLB/ybXNlj+ke58uz/3kveXzj+b/APzjNq76tPrfkeJbm2u3MtxpBdY5 IpGNWMLSFVZD14k1Hao6TawXlh/I/wDNn/qWbz7k/wCasU2lHmT8vfOnlm1iu9e0mfT7ed/SikmC gM9C3EUJ7DFbZ5/ziz/5NeH/AJgrn9QxQXoP56/8486xrWtz+afKKLcXN4eepaYzrGxkAp6sLOQp 5U+JSeu4rXZUF4yfyP8AzZ/6lm8+5P8AmrFNu/5Uf+bP/Us3n3J/zVitu/5Uf+bP/Us3n3J/zVit voDzz5L8033/ADjtovl2002abW7eKwWaxUD1FMQ+MHenw4sXmn5M/lR+YujfmboWp6poNzaWFtLI 09xIF4oGgkUE0J7kDFJL67xQ+afzt/5xz12/1+78y+Tokukv3M17pXJYpFmbd5Ii5VGDn4iKg16V 7KQXkv8Ayo/82f8AqWbz7k/5qxTbv+VH/mz/ANSzefcn/NWK27/lR/5s/wDUs3n3J/zVitvtLyBY 3dh5E8uWF5EYLy00uyguYW+0kkduiOp91YUxYp9irsVdirsVdirsVdirsVdirsVdirxj8wL+zsPz 48rXN4QsC2kKMzdFMk1zGrH5MwOYmU1MPTdn45T7PyiPPiP3RezkAgg9Dscy3mXzl+af5RaB5R0R tWtdRmZ5rhYbWylVTs1WI5Ch+FV60zAy4uHq9r2V2vk1GTgMRsNy8myl6F9o6XM0Pla0mUAtFYxu oPSqwgiubLH9I9z5dqP7yXvL5b/6G6/Mn/q26N/yIuv+ynJtdO/6G6/Mn/q26N/yIuv+ynFaYD+Y v5r+bfP9xbSa48KQWfL6tZ2qGOFDJTk3xM7sTxH2mNO2Kaep/wDOKPkPWv8AENx5uu7aS30uK1e3 sZZFKieWVhUx1+0qKpqelT88UF9SYodirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVYt5p/LTyj5o1CPUNYtnmuoohboySyRj01ZnAopA6ucrniEjZc/S9p5sEeGBoXfJPtL0 2HTbGOygklkhhHGMzyNK4XsObksae5yUY0KcTLkM5cRqz3bJb5r8k+XvNUNvDrULzR2rM8ISR46F gAa8CK9MjPGJc2/Sa3JpyTjNWxv/AJUP+W3/ACwS/wDSRN/zVkPy8XN/l7Vfzh8gzqOyt47FbJVP 1dIhCq1NeAXjSvXplwFCnUykZEk9Xmf/AELL+UH/AFapv+ku4/5rwsbd/wBCy/lB/wBWqb/pLuP+ a8VtMNI/5x+/KTS51uIdAjuJkbkpu5Jrhdu3pyu0ZHzXFXoUcaRoscahEQBURRQADYAAYq3irsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdir//Z uuid:91f869a7-49a3-b04e-bb5d-b31bdb5179ca xmp.did:FD7F1174072068119109D8284DAF73E2 uuid:5D20892493BFDB11914A8590D31508C8 proof:pdf uuid:b793da81-910d-904c-b6eb-fb165ce2789b xmp.did:F87F1174072068119109D8284DAF73E2 uuid:5D20892493BFDB11914A8590D31508C8 proof:pdf saved xmp.iid:0180117407206811822AE61051C92AC8 2015-12-08T18:34:02+01:00 Adobe Illustrator CS6 (Macintosh) / saved xmp.iid:F87F1174072068119109D8284DAF73E2 2015-12-10T09:25:38+01:00 Adobe Illustrator CS5 / saved xmp.iid:FD7F1174072068119109D8284DAF73E2 2015-12-10T09:33:53+01:00 Adobe Illustrator CS5 / Document Print False False 1 987.777778 987.777778 Millimeters Cyan Magenta Yellow Black Default Swatch Group 0 Adobe PDF library 9.90 endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/Properties<>>>/Thumb 26 0 R/TrimBox[0.0 0.0 2800.0 2800.0]/Type/Page>> endobj 23 0 obj <>stream HTˊ$DTN[@+!F-gvl+=3"annߏot?o}co+Mc<|ϖV9rKV{8ӪogEm% ß5d}V:g|d,XJ=kQΜr+g[gr*Lˁ<9bi츤usjJ􄊅-kaQbdm^HVj,Pkj9+?rgxtTÉRWD֕˖"6wXJX֙r^7qVy 9$!C%Έ:U{kkѓ>KXvXI޽jm6[goТGTh6/:M{v%RzjZGAgӏV)qR%Ά"&]2U8@ kM=yJ:tgtR:;)^DIIym1#.ݹ\/V67M IBw# 5 0]*pV觢,RE׎)-dEu(׺oSOC*I",>۾-|pw;`™׊~kEhc;E-%*&Y*NBTBBY,$@ՋQ C%O#DlXJilPg8;^y DI tIv:?E%Cϖ8ާYVΤqҚVNeAJPoC43R%9ȤamnIqVݟoI8 &R9`d+e#4sxf6*!MAV q?fB)wC:ἨB$>{y1ҵQ8X*QBS#D !Yc>f{q>KOhei5[V;l? OJTxW2`Y.#KbҕXɸƯT5iZ"{pbO="z9%)o^G/8;}[$Qb}?'VVOTvEHm5I |wli ?~˛R$ƥƗRPE nRjPgzޮKAUB@R{H.N`w_ RiHA\56qiЀ[crfbFrUcz<IOUswKQ%q@{^#([;h[g,!-tr $SLG㒃o]^pI/\h6"Ȗ?4em&rbPjJqEDEFX!QYYDam!1tϼ?1m7L[r@o6*,,BDvEiSL Q\5S[[,p'^ hyU"5*Uʰ!m3 Q~K!W5%Q݂ .4-GA]|iy&\,՜ %u! A)\ͰpRmE= V0jOq!jz]ʹ0Tt+ըQj5RҘ`)Vt5,NdUu? !|YO{ʗlNݠ&j!5Xxt _+a%U m04dM^mSO?PQuԒ%zx6}~l|O\h:,eIQ-[nФ˼Yl]72 ,; Wb5@ޛ@Y`EBh' dP$z1Rx>_&rg,eRSYg|d+N &qꅸJUU" Lz`e$gWL0@v/Ft =9A{/ƨR`MWUФ U׵sìt#S΋ɻI(?~R+H 7 <jo%!ZF>kFЫ#gTY:Qa|Tf]N`9<\ ݛ6_.GdDPLΤMW4zAiP9IH);;B"1cD"F$N$xE*> eU endstream endobj 26 0 obj <>stream 8;Z\ulOC`S%"oKFs#_7lGVlU&0IAnM>_D>Dd &G'G*>EA8V73&;\F+I#I[>l6kG\EZA;AkJ5hEmkh5F7\W^8*n@d8Tj'A3-@Y%m4%mt8$SsP,MA3L=69CNU8Xkg UpCe)[t[p\MSJ3a96Nm1=%7J"'^b"EF<6_'J'oQ[Vp;2&ZJ6SWrT7]BH*hYIaZr$QLE8Ssa)a7X*Yp4TGH^z!!!#6c"d=5,Y'B~> endstream endobj 27 0 obj [/Indexed/DeviceRGB 255 28 0 R] endobj 28 0 obj <>stream 8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn 6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 21 0 obj <> endobj 29 0 obj [/View/Design] endobj 30 0 obj <>>> endobj 25 0 obj <> endobj 24 0 obj <> endobj 31 0 obj <> endobj 32 0 obj <>stream %!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 15.0.2 %%For: (plusvic) () %%Title: (logo.ai) %%CreationDate: 10/12/15 09:39 %%Canvassize: 16383 %%BoundingBox: 0 0 2800 2800 %%HiResBoundingBox: 0 0 2800 2800 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 399 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%CMYKProcessColor: 1 1 1 1 ([Registration]) %AI3_Cropmarks: 0 0 2800 2800 %AI3_TemplateBox: 1399.5 1399.5 1399.5 1399.5 %AI3_TileBox: 1094 1004 1706 1796 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 1 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: 642.875 1718.875 16 1620 999 26 0 0 43 135 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:1102 979 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 33 0 obj <>stream %%BoundingBox: 0 0 2800 2800 %%HiResBoundingBox: 0 0 2800 2800 %AI7_Thumbnail: 128 128 8 %%BeginData: 5054 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF %FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFF %FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFAFFCAFFCAFFCAFFA8FD78FFA093 %93936F9393A0CAFD04FFCA8DCAFD70FFA06893699368A0FD06FFA093A1FD %70FFA0939393699AFD07FFC98DCAFD70FFA168936993A0FD04FFC9999A6F %93A1FD70FFA09369938DC9FD04FF6F938D9369CAFD70FFA169936993A1FF %FFFFCA9368936993A1FFFFA8527D527DA8FD05FF7D5252597DFFFFA8527D %527D527D527D527D7DFD05FFA853527D52FF7D522752FF7D527D527D527D %527D527D7DFD34FFA09393938DC3FD04FFFD049369CAFFFF7D27272720FD %06FF7D20272752FFFF7D2720272727202727272027277DFFFFFFA8202727 %277DFD0427FF52272027272720272727202727A8FD32FFA16993699393FD %04FF9A68936993A1FFFF7DF827F827A8FD05FF5227F82752FFFF7DF827F8 %27F827F827F827F827F87DFFFF8427F8272027F827F852FF52F827F827F8 %27F827F827F827F8A8FD31FFA09393936993CAFFFFFFA19369938DCAFFFF %7D27F82727FD06FF7D2727F852FFFF7DFD0B27F82727FFFFA82727F8FD05 %2752FF52FD0B27F82727FD31FFA06893699368C3FD04FF6F936893A1FFFF %7EF827F827A8FD05FF5227F82752FFFFFFA8FFA8FFA8FFA8FFA87DF827F8 %277DFF7D27F827F8A8FFFFA8FFFFFFA8FFA8FFA8FFA8FFA852F827F827A8 %FD30FFA093939369939AFD04FFA069938DCAFFFF7D27202727FD06FF7D27 %27207DFD0DFF522027277DFFA827272027A8FD10FF27202727A8FD30FFA1 %6893699AA0FD05FF9A936993A1FFFFA8F827F827A8FD05FF5227F82752FD %05FFA8A8A8AFA8A8A8FF5227F82752FF7D27F827F8AFFD08FFA8A8A8AFFD %04A82727F8277DFD30FFA09369A0FD04FFCAC99993939369CAFFFF7D2727 %2720FD06FF7DF8272752FFFFFF84FD04272027272720272727F87DFFA8F8 %272727A8FD06FF7DFD04272027272720272727F87DFD30FFA16993A1FFFF %FF9A93699369936993A1FFFF7DF827F827A8FD05FF5227F82752FFFF7DF8 %27F827F827F827F827F827F82752FF7E27F827F8A8FD05FF52F827F827F8 %27F827F827F827F82753FD30FFA09393CFFD04FFA7A0FD049369CAFFFF7D %FD0427FD06FF7D20272752FFFF272720FD0B27207DFFA820272727A8FD04 %FFA8272720FD0B27207DFD30FFA169936FC3CAFD04FFA168936993A1FFFF %7DF827F827A8FD05FF5227F82752FF7D27F827F87DA8FFA8FFFFFF5227F8 %2752FFA827F827F8A8FD04FF5227F827F87DFFFFA8FFFFFF2727F8277DFD %30FFA093939369A0FD04FFA09369938DCAFFFF7D27F82727A8FD05FF7D27 %27F853FF7DF8272727A8FD06FF53F827277DFFA82727F827A8FD04FF52F8 %272727FD07FF27F827277DFD30FFA068936993A8FFFFFFA19369936893A1 %FFFFA8F827F827277D7D7E7DA82727F82752FF5227F827F8527DA87D7D7D %A82727F82752FF7D27F827F8A8FD04FF5227F827F8527D84FD047D2027F8 %277DFD30FFA0938D93A7FD04FF9A8D9393938DCAFFFFFF52202727272027 %272720272727207DFFA82027272720272727202727272027277DFFA82727 %2027A8FD04FF7E2027272720272727202727272027277DFD30FFA16893A0 %FD04FF9A689369936993A1FFFFFFA852F827F827F827F827F827F82752FF %FF59F827F827F827F827F827F827F82752FF7D27F827F8A9FD05FF52F827 %F827F827F827F827F827F8277DFD30FFA09393FD04FFCA8D936993939369 %CAFD05FF7DF8272727F8272727F8272752FFFFFF7DFD0427F8272727F827 %2727F87DFFA8F8272727A8FD06FF7DF8272727F8272727F8272727F87DFD %30FFA169A1FD04FF6F93699369936993A1FD06FFA87DA87DA87DA85227F8 %2752FD05FF7DA87DA87DA87DA87DA87DA8A8FFA8A87DA87DFD08FFA87DA8 %7DA87DA87DA87DA87DA8A8FD30FFA093CAFFFFFFCA938DFD059369CAFD0D %FF5220272752FD5EFFA169CFFD04FFA09A939369936993A1FFFFFFFD087D %5352F827F82752FD5EFFA093CAFD06FFCA939369938DCAFFFFA827F82727 %27F8272727F8272727F8A8FD5EFFA0689AFD06FF9A9369936893A1FFFFA8 %F827F827F827F827F827F8272784FD5FFFA0938DC3FD05FF9A8DFD0493CA %FFFFA82720FD0827527DFD62FFA8CAA7FD05FFA8CAA8CAA8CACFFFFFFFA8 %A8A8AFA8A8A8AFA8FDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFD %FCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFD %FCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDFCFFFDAEFFFF %%EndData endstream endobj 34 0 obj <>stream %AI12_CompressedDataxܽz*9(zn{ ` Lhn`]ߏڏNa={? *T*j7=fHy^hZs45oj|X.= 7F2%@C6lSnY#<,·[_hwZ.7Ӎw0wpmQ da[1-X|#BG,̴(E57k!G7I A'qXRz}$O1O1`ʎ>=`.7qV]hAmK[6c8F#`2|C c,3O0(Q'ƒ840Uu0y5z>$IH= 7gz SkvrOwާ&@[Xsd}k0`1 xe 5ixFOal vf43ֆ?h,y)lU@`X4edџܿo Xe̯_r cۀj/mD[MP [@0 j"A{`?ZwffٟѯGcbH1 Z+csr>|$@9yv cY 7n%K}@%S+5hz I7lZ6y>xԅ/Ę'P,WYo^?qar}σet|9( aV-zv c|? 0ϝ*cd`_C0$@QA܁}PH8X t;jp\Dc$?*g]&}7N|10wB-c-`枆 r x0p/6}ϖ}@+j?_5ZڻC'If4M6Iχ==r%@ybuIcv7]|*; =oxw1\܌XNkYH^f=h 6` > NŢH&Ӏ|CinB]QLA!L_,5xvxD4GoY{aGnUjg"͚8{Fu<ZZrz=rc+qmPqo tc)㗤~9_S=6hf$MnOў~C`=E>7|4ӛ|IU?tF87/n]Ð!M6(?VCj|Y||϶3;3咋[)B(pVTtI,m󸮒 fCCqs4X~ciEF{f`H  `A$b+фoޞj{JrO 22a=S;I_iR͚u" k IZ.qN#fES \8 #W 绽 !ʬKh$֊,Z٢>.Hـ0zBTy_B"x!7*Y#ZMq*үf]ƼbcHG@ Uنrֺ|;Dd4)3)AUdOP4%U dP%NPf-R 3h|(jSZl)&FFbQQp n7zlA3|G3&ZL`6 F3 qbSoyV!1Zv\w]5 ـ$.HbZKbrHUH]2c`J-̶ڨ52i,nht3IH.AM8 ?Cܬ`BΖSŴb<ċ>Ӄn;@7w 9ŌX޻D*ujh8'z4 D9 9h9Bͪh| 0 'j5h>;-T Rk_$_#}+|3MuRaˡ޼e75?!n"IimexW:(~;ʕw[E{i<1X!?zde'>Y6?"퍁 v?M鱬>tFQŞdue߮tZQgh%F~ LA|qviyh!Sm;҉Y` '_zd[nv@=E!6 [FE8>`~ɧ;MeAjM;ځyy[RܦJqV0TiS~ !Rم#aY=)$o&; D]b|+T9wvW*Up;]21y; ղ! h};%t5`#*~ >"uQq'oa0dחXyrz~ҜS5 1BtNVs/1YYI#x#6WLq Gh}{ (҉ޜi=CP7ājSۨ>N?ԚHa&?;2bC4%pJ Sl93ve]Lۨ'~\a2k+S7^49@0崽X.E:{taeTfNuew7;\oȝءAO={?8>'KmrC=_feT=2"X\w?~]H̎ˆ~74O9 e!&>5uÍB6,n&yvy6g XJZ_pMts5a>C=ph,}{ C.tW낵:Yd t@Y GʟJ6lKÙ}~9L<Ȉ p_}*\~"lcɟY#eV0%g*v ]7XT-_ZX)^01<\lUII S4ix{K5c=ՖZ5~’}>7_^0\/Xœ 8"I_ q Ix3;GO/%R}9-V[Er>Q1ROkͼ<7Xon0խwY(Z]l,YL5zG}zw9b=`kl9s5@~c(Qi7cc*ӷz-Ҁ0~BQ}bInaToNQ}1ͭYUIwtLKiںin~]FoGÎ~#Kbj]Li(Ʈr'zsg{~c)jx|P=RW}Ls0ܙb[v M.2X_OFmgyI$chib^poI Cd*~V{ $Yv@<C>)s{ oͿ 0_:dA^@޷6@d g%+e5$$,7!Zfzth{L-^-ny=KӴ'v29|طNo%жnf39›KOS9-2V&k FfD۲|MQ#21"#$I[b2z$ŜQttKQ\Kp\T)c扡$K˧+)~}Ot AY0 t{:`SC{fV\\ح~fU0+D-}3͑ (ro˻Bqc}ͮj1ZCK)Aځ Z'-8{dwZ4UҴO6Wac@kNpoSMahD` 179-fRJ#fb(oй2=*\vG4D'ހUTlt2k3"sSB&䩼@AAcAPG{EKV'uaZCXLTsЭg6] |%'br&mݚtT3|؃EZg}07QuP>]rhЅF*uKqZG:@|e r%؍0\8P^4IG "\n%cD҆[,0 PpW7kLcSݵ1g)-|qFdLtmoQErbnOXd,' ^4Mx+j6 `ϣ.a`.z0ԯ o1Y'^4q2c;֩ߺs|"& M#7CJ>+D&Ͻul  !t%jF8yPEDJGb fyՇ/]EXUxrsj Tz^ 7A0.eZS\7>n&yt(;&b<K􎚭\%{VVfKz6 +h:ꋐSgoX#~T4IBiBOcTzx^XSBæB,q-܂9:BڈQ˴:b=IH8jfs/ùRWZ>MMRf(*`etc@,ļ؉6fNz+6Mn4A)N pR03EJ+/̏h^Io۪5Q6ju uZ P I2Bhrkl;]-Chm&aGq>4\yf?S[XB\.i#f6F36~ڛo_CC`Qj7OwU`4 6C o)=cb[bF ZNi ##J:޷;/4RhVB6 -2zTm[SӁpȄB!UW6`ǁ^*a(=fJdғ IgC2meP7\G=[ .勠zWNԈs8ixX4PkxZpE<Pl̋x:czꄋۊz-qC>"؎Ʈh 9VLǗP15&vN/`Iq}l7#=զh‘Ӝɖ=I} ?+XE%m!EO-gl7{5L2m?W<t8(g)jh#W-Eg31l4`ǿ)7[_3l` `c1O0ua~q`r4 5k#. zH8TMM `Kv̅X|;A[,ؙ:^bvתx;v~,WpO+_!16̀B/FR=>דu?70^oA"pgXiQ40w=?(ߙ<>[`5R3gs{5ɛ<ēu}Ȕsw\~}h ~QJ>3Xi:u7̸ϏJlo=.ПDm2w#ҙY,ӆӣh:hr{&O5F&UV01_,M}vt7,g #+2Xѐv/[oKF^.Z[=giIoynM'vms`!@'HD Fio=R%ӭ0iph)Aʍ%Z:,^lY}H݅ȎΖ>吺`Ribi?$H!6}'pMYu9Dq2*+"*e쎉RʏϵpN9,Һ͆",oijAuqJG-?3(!ѽ(ّG\pZ~Ç'*Y٢M# :Y8V^ Hk(W@M% EhK7wUj*m+csZ\E0Xw{:ZJVX]]2H}%<!Z펢HkK`xn?\D{GZ Iia "F| 6ы#z,qߙC $LwSkNJH7}Vi9`)"1YakC x:_ĭnYz" uo*!}" ^ixe8?&…uRzC|uޝ>#ĭg&JOD?܏)<`,^g%|`'c'C 6ʋQb ;OVin%w%wul*;deњeLr:~iJ?}QzJ&V ilh}re7gFzV5+|RYVZJ3$i}{[غts/$7zG\Wqך[o&0[6d?wu)s@$Z|@s8;HXf᜝ړHCiM6Hݎ2Rsq ZGK[0͟JHU<1Z.H"r3&8f;4ؼ1X*"EܦxsG~ / G+"ʯH0RH/eQ$ -X_fw7a[&tA ~VsԙvPZ GDKx~#rE#\8 +>M_I7 kS-]kR i\0A!}|b,ߟ< 6^[+1#_?dkWb0쀱. Va8F80'" NoF孀?%NM.$WDGO.;IҕG#? $3~ҚzIlHds&\'Kn (>B^n9K+9ʫ}@y-6սry&Xx7Gފ SfQ@]-JUuvv]ehWlKV%.W% 힬Tu ` N44E}hUަVƥ`URG¨nQ3{@Pś %#>T8A_},7;E`0,^wh :,D6?OT[$m9xPh)D&~gǼ'"Pu yX\=#&9 k/rV0 S=EVqb`Tb0%TERtDnle֓`]rX1v.AOtʟ}=wL:q,*R<sqjXX4l~|5LʱA:jˁBr20B&-MlQLî;ejHS)F8>+q\NOQIc6fӲXѫ wgʪr 7em0J P*8 MR:nA:}0X ԗZtEK hvuiQG c0+ і&% 7b yR`PЏ@0_@~$f씨K{^ κ9ZrH<ۻlrCHt-KAʳ /\{oIgxf~TɁSUd4b.q9b!(0|LXehK\Iӌ.nfj,adcF#@V @;$ 5r]&Y=ۭ'š87_Iu*U҉ >~ QEzEG- sm ['(ล}4 g"\ŽgY-YNW i Y^1֛ 'f_nսi45qRфh+BJΥ) ޡVyׄ7 ΋U ڀ-p`i4juޮa*g-ָnmrP! isJCbUsAOg(\8m86Tׅ24{N׆*0nRmE($@G><%a( ]E4,H7,iWbV*Z13iVsceb_G5tN%#DqK%G$zɊNZWM-k@Iǟ̤(3y5|# STHe^Nڽ4XğoO119'wQM/0>% XpLD]R K;/,fb_9@rڬlyP(nҽb+U/ ::5t. :b:<긂?%: :Z: L9|Q z^-xI zJI5ttx%5ttjk+-vy zkЩW1c9NR7-NWGtDzA69EFNȋ_޺\r=8t2iqR+SǓS0>i͈9F. z_L钮GȕJb;ԢdM. g#pn)]CH&uYvj*N(%iOQ02 g!VhIJ  BdЊ.2f퉐-1S+a<)pc~H(L8IJ,qviʚ JUk''U,/:>'R.M`F|I-#ikOD0eD((/#O)S޽8b7Q,p 0q9^R脪4gtٲeibHXT8/ZZݤt=RGN$nر5LkQf;y\Aa!+M2ˁו S 9u2y}:aCaĦujdۯth1dۯrRek<'p2s0u9qjUa[Q*f>:X44i`|GYEjԁx^]"oשHBE*i\"JE*syE*rTX} $O~ ,CSu%oxZ.+õ gA_NXl1?R w_yR)D,c2"4#/.CguNlSLȂń'q]i2+=Zn@wg0]!ڛ 谈MPS{.ntIrҷ#,hT`Ϣ=dע7{ӄW Vxx$H;x7- ou2gT!YDXetY]"NZ,̯}NeQv/Oه7R]V("Ej+ [PYr]ORF /S;KT.Q{I70Uӊv3*uՊݦ>rukT}|G"Rӧ%D^Zp+U\==Uƺ,8Wvg>Ap̂!Ǹ v% U-vr{doWDyiDbI>j|UZfŜ+;Z7b}$'AiNgn3_R )Dz?7T2^!;9iuوɝvv {F]&녘T[P ~TXCi%/t]+cgt`N] ]l)OUmk Xkm[[2rG#U^`*)d8s7'ʕK jV)p.?%sm"QI:-W,B vxu o2 gDϸN}2@]jYpU>qwb wgݔwr=My.C wwÝAj^ L5N_XKeFA[+)\u%\!8Y9We ҕ־ ]68o+0ٸo*"iRbeLjEjC{^^H[X::w_٧x_vJ.a(*N1jV 5nictMyָyv(RvTGuÝZ[Æy#:Λ:}*%v#k\՛ =e3PѴcEz.k5>²~n%cJi|=\|XGExK?9a]{W!oTDzK>GJHNJi.mbc,3J.ZNެJXWF;"R#-7'`cU)84wAe%w[GfBJZUCZ)"w)RKyZYbzG}ӮwXq 2l:5Mw%2)dDWGQVVQӑ6x2A\BVQc2r敻Lo&bot[Q<n^ΕEw$#y NSR4ҜiUiJszuӉYZ=p|9zݭ^WidN7'ʭLoscŮakhu`%- mTօt=ckk{F=M&:Xq`@bZ^KA5KKW|L4B c+aÀYD訃0auusaWX*Gs^_TeJ6`)}]dN⸤nPH=H%5VCspmqK(yj) 6D}ݥw͟Rn"JkfZ]i)֫dWf-)֙#9S)H;*Y:ߕvjxU*ʜqB5O[J~EH i7식P jUHլV- 'U*PJ'ߨ'#[#^Gu֥|gv|WO#pK I?t)=cS|_yΥ|Q)JRMd;3: gCwr%½~g uƽ~CP½~)O gC{.?JϽ~+/O\(CϺ/zd穣|i\'~LsS'~7p{2TsEX 9g*^zcg'JQ:DNtv55S;>sCpR)1Xs #.O<_JLV?i@{+ROO Muڽ~%&{ߙx}|=`]TzKueO=y!sٽ~|ɕla4^1^?m9v{o}|ZIz;0{½~y_ӼOYœqZ/W^~,{]Z+/O*/O\9^pߥ,ɭ~ǧ W%J^sٚċ-ã[+ȱSOtBSwY]IV?qK1ŤWt dnR`NV?ig~4,g~= VV?,xѽ~g0g8hGwuƽ~z-/O]9T=Pc XpvY}.ME'ދQ5O[I LN=',{k %P:gMo|̐ѷߖ2QXOUvdtݚ` dg&OƕH\DjPO(V^2@7*Qzys&SfD)#Z6M6_݁xĻA| 5L~#'diw} m>-r Mv(/ѕ}oņ&h^3&G9hlmXq7>ۑNM3,}ML`lr?u&tey>rtȄ|$W?h49ł) 5{:Mo_ًV{{}O( (;|ۯ !n?= H}aYmL H}f*p&w=y='<4=07.Bss vUp,4H(]dxqnh/?<éˋ(F$"G78;@2i#aߐ? {!7^^XX}@.Xy hyqN@,}k.Sg>TSS6HvNQ.&_`_w2VßK8|_p{dMZ- 2 UB쏊pr}ܻD1X|{s? T?!n,aF(_6wK' ]\8uSxn5H/}E_$͏{oANRnwsyWQ,Q\nCå%/NI~3"KKeþ[|| hYO6r%<^"[aP;>*PFz3wR/;YAH}ss %XොZs~S*#I@B qQ! vtw'=Ή8,ˤꂢ°.wcrCE/.`|/#B  )r*33 0Ų`YpH˔4Hfϖggvy ~(eFz"osRMcz)0߽A ,agI`b"A{p7͍~?\izr}N3La5K22{0?L3{\fg ֲ+LF'b4``j![`[4|s),\87M/2x\4NnJnMG"\ =c}2iPh ī @mj]/QZ>=sr̗JwL+Hn$ߟz:AL/vR<~0os¶{#",2ڲ h1rC5Xu c ZH(Vz; z1#{I1'Աعb3u]:\Ե"Q'?bH o&4ʈv4ffL⺺tYk/.WPlU &{ rY_Djr8~Є>]zwCCw@L2e_>rp٘ü]9\:[P;JG0vs:y__a|3뚆 01ɘ!c T]{s2õ.;r\ew~sP JXOk8^ V[zImgq6ezA@_~PWtz'nr[5]t$Ź=*B|PNDt|k|%vZ?? |-׋95HDMv[cO!rĭYerH'a. 7r{=VU\C9=`~@´oc~`XkN籗vk`kB# OY^grz:3gy W&I?5yYי IKfq qpݷeWe.%l9幘w3Mܖ/2sz& װ'Ѽe|(@u骇h(auqhA߈^!{UYъF?cQbN \}(PIfc{YۉNt%$1X-7^ *rPQ,E\.6+04sogiLKož |@,Roܹ='jLX:G]!kUܙ6=lЊLq>$\lg?Ê?;hڙUNFGщģi>?; {|Wx4l>?7h?7G38pv?R.y78+RE0{YVxT&LtviLO߆6E ͜˘6&܁p  X~{F~3S%?897VAC> өgxs;Vŀ3nτ'p ߕw)Dk{߾v}J{X,vP ;݈K` WbT{f>L}736?z|K)flךIH7wܪtt ,,Ų5\,32$< >,G'BGLF:0'1=k#UP$#}_Ma,RfJ$@w gHm uO`i++@:`4>aǨhM+H$ {I r`~.KQJU3t$,r*R&`pu: .0nX@ G"}@² :f#~YRYL&NJ H=PQyn? lA;s97@.aU0KϣczԧAPyw";kI9/\\=\ : @ϐ:wkSBRQL/%39B~ys+K-\.YwL "@(,I`FQic7t.=L],Do{BW7x O'GŊ6'9$"F77S4饧\QN福XGw:`yGӻ T5\Xc41~h^=mN3_`ie9\ @/h:z>y""ʢ|;Zk |0F$?_ISI|O\-V9[jX9,($Ve7HƧkk1YN~gJ0#W΂Ţ0їXTLۨVa|gb"\X4/-?^[R aWkC݀W[<>HloCm8pAEAS"v_MYX3h~\*mP=$7 keŒ`A!d$|0' w}G(ldL}vzڷNG*i{Okg5" 342EC3Hp9ܲMtvi0 ݋mr_XGU#ՒTw7^V>M_ʡI:$|qDQ}"!0|zsc;se@zT"3՛W B'=A`3m`~2O),{Pk>Q9ߔ!P4%Et6 /@HST>Vzo}f!S͘2C^| "ʾcXiQ4 MIsk0'-.l>)x^omC<;Eë绸1qD 0ft%KE7oqlw"^춊wM1r~`,O|z잁/KN::dw.C4StsqrpdDblX>wO^u~3r-IB2Wvs`ѡzf/j?hǎsWc+ lRi7~.^ExU?X^XE2G\JD9`KEAХL12#J5jqK~$puT/T?xxc ,zX u5$a6xZ>tPtk^i*Ez(wn'3XχJ`.˼&sHeieop'#u:p>ê 7lxw06c&P|(ci4 @ZԆZ΃14ȟ2* /I4+ʠI>~g̷磋{? ٮ M}U^'] N0SGp_@' Tؚ;IKuPJWщ6[1Xo.g MCC3nعrp"aqg2X91'[dw9+%|BQp@‘=^ESwFZʟL^&PnO{M#g145gݲ]yzR;U 9Lkx9=9Ц3{6p|hP0+50V?du؎%R7^ | @. vH7M{Q?tPn M'=OXLfC1l1Y7|D$&bD YR>L7OC7V[K浵Kޑ+Z9̱XWĪ!|]*f _vY7/gT {D-ONX$I=fH ;# D",rt: 5GmcDP $E׌m V-^}-wL sMO9V]bp\hyɊ@KՆ1x%è:`@G {^26 [}mc v! w^&aGGܚ >x%=du%>"$8^DSI_dm Jny l1s@-pGkW:vRXb >USFUzcRGcz Nsfg4(eӖ+0I m`F&;}x)  )($aϪTj}N].>pY,-ySIn"VsU'%ȪkΝ%u"(e]Rҵؠcħ]%O\p@%.$ˡ:7rV(d t+^+ ;Nv\Ł?- Fgڀ^"3; d;Nq4xD* LQţ]]W9bQ;]y_;Հ&ħ Q*w뵏seErǹdtq+&HtykV gsdça>Avq/@zu} fN$>z6la祢M$#w򛇬\ )$ ÆJ5CӮ- x,l)C`;zVKAuu HHF(_C5cum}zz@1OC72mmh!5x[}þ0f 𒡰1O"6. 1|?m$ 0: C4H7ƌ+Ƣ I2 XK܏H 0`~ _H0GH P$F+QD_7#K-ƬC {;_HDTwg[w3VVf뜈vTS@fUbVr(Kz֑lvGyl|c H/ejiQEK[ VpDkhcYGc% <:G:@Xiˉ _i{EK͔yΣ1P;JO5\gcLLe\+r9u`^Viŏk\VV܏e\ZdלaFkGJSWM6޲T0xMS:z,ǿ)S-ա׀\ KG]z옅FD6A$c ldgfO8%&ϐwG૽Wn`Ks4<1eiBwՎvFDpٲMU1b>(6k)93r0VɴpŦ#u_JPQ>Vh]I'6F:0Vzigb4< EN <$le%qF5;#z c~Vj=3=2С"ԖԈ,Ϭ=K9GR9Ϯo)e_p7 q\|)Q6p̀fH8Jt.}; [Lh*NʣN9D@VΖ[xIf-,+t&\6W`؄y6UҤ{U_!iAtҖ"*ES),/ɏ㞊\K/L~.ʀͳ:q-DZ;" QAq=8)AThG%:$- MRԭcpqCxnԈ4Rr*[ۏ MΞsmTfb.4KLXC|]vC> |,V묊P_A$ <5GTAvBB%tZMU$ aƹv4߁ ZC ry.K *DFE ys)!DՋ:?!IK?X::ϙJ]?ߋsx*%˽jB4!)g/x6:tP9:ĔHNyg28ng BΥ$yB"$Irj=pVzH5J;񖔙 moJ%=,M$+2A rFM2'@.*U%%؞{e<[rHsY-Spн7">B/Z= Pw jʡ⥕] LrHDD%!NS8 ^I>6&'4$GqD~ y0SL|^^t.5^w =I]QMTLF_ɩtx,(! #QރfoˏAZCUfi鸆b=b!RR=h@BcNsIPN%RC)Ti2 #1dҳdOLt&k"-%eO ἮLpN+CX!oӡ<ש3"aQHe&Q b7A<+ ו:A&*5 ̌ORmLn ,UBe(]Z U3 Wx̛%#[se,>AXrtu.J*>K MAA\ p6hĔ=6ڮjXܓAҊ̠Kz-15tUᝓRmQ$2UH_3I ҳuʧidZv6G rP8xk$˞,e\s޽!: d!T EuN@aJdUNeʆ\M05bLmiGFpJi)pY'DtI1$A JR𴟧C@sӝ1bJdqܛ5Jy yqT j9xp8V{@X]R @KdmZe ݳ:CrJF @dAF_q>C،c,]H0e?04/y$tBrz$[*d9'o(InC. =ӵk|k=*K}|%u5Xii>$.9hݪ>GӥZVDQu;wT%sDȿݒ$v;ջ\@<Q8Dl#-8fn6Tߌ}C#4sYVU`_Lnh0׊j;-B BѤnu;9+p?҇p޸҈[W_oU3/k% h\1+֦\ e㐥^׆,4d;|Γ\Q=VScNu6o V#W k٢D?l!q'X5yjQ$ 9ٟ2$d+ bQ bBU/QX0#j 0T^uUzEIV[ZnaVc6XkS_G89ڰn{EDܱoKU?d)'1U懕gP):M]\O HgT眚@4 UT&~#bbH^SKB).Tt{kI{DS{~__Mtg}Ø' XΜ4(?Q&JV ,:z ,NNi@_,e<" "<4nq|T~D&;9N@oT`;#Xܩ$W)rX;l裼Cv,hO(Vn>]xB%ZiC79c@&Smc.fkgq]@#JЃ Ld 6&03_cnLc&-Jl!Q>OGq"W& ͦNM'n;TjLdc2?{cKv\e4ڌaIvomt ^Yp˺ᄑ> o>W_]޽^\>ϟϯ/׏_^/ן~BXr{ݻo9I\ endstream endobj 5 0 obj <> endobj 14 0 obj [/View/Design] endobj 15 0 obj <>>> endobj 22 0 obj [21 0 R] endobj 35 0 obj <> endobj xref 0 36 0000000004 65535 f 0000000016 00000 n 0000000159 00000 n 0000016155 00000 n 0000000000 00000 f 0000055236 00000 n 0000000000 00000 f 0000016206 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000055306 00000 n 0000055337 00000 n 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000000000 00000 f 0000020707 00000 n 0000055422 00000 n 0000016539 00000 n 0000021007 00000 n 0000020894 00000 n 0000019609 00000 n 0000020146 00000 n 0000020194 00000 n 0000020778 00000 n 0000020809 00000 n 0000021081 00000 n 0000021255 00000 n 0000022240 00000 n 0000027472 00000 n 0000055447 00000 n trailer <]>> startxref 55615 %%EOF yara-4.1.3/extra/logo.svg000066400000000000000000000144131413423160300152570ustar00rootroot00000000000000 yara-4.1.3/extra/old-logo.png000066400000000000000000000200521413423160300160140ustar00rootroot00000000000000PNG  IHDR%E'n pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FUIDATxMV`"/`e,O W`y.+T7`I@zlճD=9QHz*#E<EQE@=-%A @P%A @P%A A @P%A @P%A @P%?O5}=l/</myPz즡K;a8% ~YO Lo:)= [ B!}T@0C;E yˎ4F/8‘kA CwZ4SoY@j,F"T`O (GL=ȌW!(G?&`Gy-J%cRMEעCP0!0 -RSEQkϿ|gV6׉n_?aW뒻OS5+8S;֣[|/ŗK3z/׆wnSAaВϓ> %AcZZ>o;mvsꍄ$S ]FoކSnJڍ$j6%l}bGȹEAO$ƭ$(AN=nDPuʹ60Y$~1 i+#KI;3$(PPgZn%A ,^RZ (P_` S I#%RZ (PWf8KykJDiJ,O~ε%G Wŀ1Xڤn&)fƻ.-X%K|:Fp17o$%(g^ZtJ-Ol~ A %6=7A }߱EfN!(PĂCP`#-@PnJ (JZ (J (4}-(ɑ}i+*n*J@-c3 A"L_tnP<Գ> (AfZ Po&@PL @P ԾVntfd `wS-h~ӳ?уv&A v)8@PMBiE+`w#-@P]V A 6 uJp7FnS A yHJPadiI@Q@PMB}mokR H_1 @P9/'I%8xK;F u7 iLS-9I&48Ž7 (Ay<57ѢJwZv엱{B׮eYo7`J!uSklr9hNJ (ހƃ0$Y؂u5p+PDlŗK%kp$N0_,~?u1I@$( JA @P%A @P%A @P)(#N_C-$YcS|)_|!#tN:'nͲy$I[uzQW= ]a_ŁAэJ\2ٱVe jRm5m}hj_7nAiRߵ@<ߺzw6w=}?׺uנu|u D~(S^ϳ~uzGM D庯>^o;&>=nK~p1w^Ijgwޞ[ jJ;cnK+?-[n%Aojc\!(UZ~[5I `ۆh(pw@9ؚ0cVV^{+(UbHR{6ͪݱB}ڹ ެ`7hx3>Tz9Ȫ Ҏqz48xwm 9(U[viӑF~Q=2p^Qz[IzJ]Wt-Sn+&mU|(m9QʳÈ|aDi'nfQkaiwH츜z8q`[/;'Svێ4N6=t-|TN(=v"Jvڰ- 4Wkn8-Ɏ{T m7=zX^i[NK/nK4{6b JPje_نGN5W5,+ѱ=^]~W;[3@WU@ J[ꁒH">f/hw˟G7{nG]|f b*PVt˲t< W-"y鮑<-ݵ3HfY;@[UVUݰ 끒}Êb8?ojjm870+q$ WEyJ'@x|(m"-BT@oK-o K~_aݵqGÆdVt7[;.l`ć:]v>Hrvk@niE+GH|*.(^dMJGD#qx[ۖ?F"k1]j~gA:۱i)dUSA>u8>Pr 6,,{p^e(r?4z mny{,ڧ]ozCS vm+ ָAi $Ьz~ҦO;y֌l8 JiUվ_+(ae#4~RP[v{Zz0 bbuWe_g!wGF5h..oRnwƥ؏IKxm{<Űfz+{=m} _bqk{7vGzj5I/]sWҲ!_Y}7[zϷr#J~ob_~hĤW~?rjvh}Y5T;ժz$^qyEG๖R|͑$XƩ;wwϯ"šZpq64pدǂ&=aAddQ42(7d~P_&iGNeYyWV$rZ LZ%(cH$Աn\$(UB H)9[qweS6Y 4ЂYFĤ=Xs/9 ѿVPi)05C.UH_ww M/ղiZvBKv:NÒw*݄hR<Hhp9霤6׷+$9:~%h Adobe Photoshop CS4 (11.0x20080609 [20080609.m.379 2008/06/09:02:00:00 cutoff; m branch]) Macintosh 2008-05-13T13:58:36+02:00 2008-05-13T15:04:28+02:00 2008-05-13T14:35:59+02:00 application/vnd.adobe.photoshop xmp.iid:0A801174072068118F62D40F263793E9 xmp.did:0A801174072068118F62D40F263793E9 xmp.did:01801174072068118F62D40F263793E9 created xmp.iid:01801174072068118F62D40F263793E9 2008-05-13T14:35:59+02:00 Adobe Photoshop CS4 (11.0x20080609 [20080609.m.379 2008/06/09:02:00:00 cutoff; m branch]) Macintosh derived converted from image/tiff to application/vnd.adobe.photoshop saved xmp.iid:0A801174072068118F62D40F263793E9 2008-05-13T15:04:28+02:00 Adobe Photoshop CS4 (11.0x20080609 [20080609.m.379 2008/06/09:02:00:00 cutoff; m branch]) Macintosh /metadata xmp.iid:01801174072068118F62D40F263793E9 xmp.did:01801174072068118F62D40F263793E9 xmp.did:01801174072068118F62D40F263793E9 1 3000000/10000 3000000/10000 2 256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;939DDAB6AF1A41D058ED32CBB7E59018 600 400 32 32 32 1 2 3 1 600 400 65535 36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;4052212324FEE53A0C9873FCDB723B1D 3 sRGB IEC61966-2.1 (Linear RGB Profile) 8BIM,,8BIM&?8BIM Transparency8BIM Transparency8BIMd8BIM5d8BIM8BIM x8BIM8BIM 8BIM 8BIM' 8BIMH/fflff/ff2Z5-8BIMp8BIM8BIM 8BIM08BIM-8BIM@@'p8BIM6nullVrsnlongenabbool numBeforelongnumAfterlongSpcnlong minOpacitylong maxOpacitylong2BlnMlong8BIM3null Vrsnlong frameStepObjcnull numeratorlong denominatorlongX frameRatedoub@>timeObjcnull numeratorlong denominatorlongXdurationObjcnull numeratorlongp denominatorlongX workInTimeObjcnull numeratorlong denominatorlongX workOutTimeObjcnull numeratorlongp denominatorlongXLCntlongglobalTrackListVlLs hasMotionbool8BIM4FnullVrsnlongsheetTimelineOptionsVlLs8BIM8BIM=XlogoXnullboundsObjcRct1Top longLeftlongBtomlongRghtlongXslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlongRghtlongXurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?8BIMADBEmntrRGB XYZ  ;acspAPPLnone,ADBE cprt2desc$wtptrXYZgXYZbXYZrTRCgTRCbTRCtextCopyright 2008 Adobe Systems Incorporateddesc'sRGB IEC61966-2.1 (Linear RGB Profile)XYZ RXYZ o8XYZ bXYZ $curv8BIM 8BIM 'ZU'>JFIFHH Adobe_CMAdobed            U" ?   3!1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw5!1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ?TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$^mFVE7jè"Z\mv7=9 -WςVb't|K YYZ9XnAWgIxc'eZ>@>9~/_Y/}/BИX}C9~EY-i׀|j;u^~#[QlYדVEGuw10AkM/:Qo?ȞVQqwDq$R4I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$I$$I)I$JTI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$tVt﫹6RNS6,z}kt /J/=޼S4匆}*IN8!Pq ]5aW*%/]73Xp2σm$BKƿ7,*Rƿ7,*SΏgz'+_EU8cW5п7Ofn~GIemp6>\Y`̷.Wyy!F#*I$ZI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IOTI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?BõךJ/=޼R?>A~ޓkʸ_f Wiό^嶓K>/I$q[2Mmc*u.ULkͳk:sQsq˼1-O-6Y82:OgĿ֟eIV'w>$^}iLX_nuX?u?2.m$U.[?boկRzc.öfU[_Sk^ D_Rzcweѽ߽:땻 +rzeBOlBMNNҏ^$S*I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IOTI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?BõךJ/=޼R?>A~ޓkʸ_f Wiό^嶓K>/I$yoJKqSK]MޕZGzjI &(9Nj|N8lp'i$H-^?s] ߺ]ɼA+ޗ/S[WrfL<{6y//zLobKFoQ..Xʙ )7Ŀy|8I+*I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJIVCY`^tkZs?Ǝtcg6);g  4'a\7:8]gHmL%݋ѭɾ?9Kt?>2 yV5yڿֺwZ9yڪf9PU2Jd^k)WIۿ`3TȾ}.KC]d;bJc{'s68,$Jw%I$JRI$a#*_&1}]/%g,?LՕ.Xʙ *"|KǗҋ$I$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IOTI%)$IJI$RI$I%)$IJI$RI$I%)$IO 0ks<]-~jWmؽN5UEWAT߸o>byL|=8IJIDY$I/΄xknmOvY֮ZuMI,ipb|@d_pܮnIHRI$I$/*_&ʿ)9C|WK~qyS7!eq60uv0pMƪ!/RI%yTI$$I)I$JRI$I$$I)I$JRI$I$$I)I$JRI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IL\ֽp!"ABųOk,6; /c~LcVH|2|/~ p/O҈v}@f be6$=~7̏?Ǔ_[>2v&KI`=Jv zGЬo7-ۻy"3 l,ܴ'3r2=]&vg;Sױ/-*>V1{z><\ύ ¿ŗIXrI$$I)KUw_/^/kϥ/3mz{j-Fk߆eh%pGE4<AO7.WgJJ$GGɎ9!(H\f O>smtDmx$N}nggxIkN|Ħo\8ߧ17tGFlg.]Eȱc^qC̚X.N0% `~,X98/ғ$RI$I$$I)I$JRI$I$$I)I$JRI$I$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?BõךJ/=޼R?>A~ޓkʸ_f Wiό^嶓K>/I$$IJI$RNӺnFnPia.n{5mp?<7 0]pS2K$9<xcMI[>A;6O1E$k4!kDakGsdE>C5p. eBͨ}dI}I?۟yPhsEoVhkoI^'LOKFs42!o5S߷ӱ> H ψrHc$\p$Jͩ$IJI$RI$I%)$IJI$RI$I%)$IJI$STI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$?BõךJ/=޼R?>A~ޓkʸ_f _iό^嶓K>/I$$IJI$RWG.$DHQe&5!H~.;ka;؃#v_?v]1̕(d?/E/c+W݇'f?:ޭ22qkOm5}*cϳonMhkCZ!@A:Y:WCͰkG[=HB8̖3rDp1_:.;q3?̯K%EWqG;3'^U0MoM_ˤCL+ˌ1W~~5vUSt1QB[ 3 c0rWg,?LՕ.XO4ƞ5Pr.Ŀy|(I+*I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$Rdͷ;km >Mo[}ht [k*1w;u_Ps7;[N{%./SI*@I%)w ⾡25Yo~b7F9n#SOnN("K>. @cq{WVmJϩ$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%?TI%)$IJI$RI$I%)$IJI$RI$I%)$IJIM4qaNDIJI$RI$I%)$IJTzD}bqxd8^ӵlfy$Q1!( '+z_}XCo]:I?t}}O1Տ6ۯ&ϪtuGt%Gد_$xXx4 |:YH2o)$ $6ORI$)$IJI$;(_c*i0  JHI$$I)I$JRI$I$$I)I$JRI$I$$I)TI%)$IJI$RI$I%)$IJI$RI$I%)s^ǓF. Z q s6\M_B\~j>0[q0CTkmcw*Jt:[/7^%^v9nJ{?U4n=;*J&+2hFUOүO tbtcYk34=VgWgly'Qf52MyŰR޳oڿ= $:/\}w .񑏽՗ZC_[^m7/5UkĠ[ZcMMs*GSleJ~~-XN=[2leZƽ?5A~PY?ie8轾oyZ?Y>uetڮ.`uliWlzm/]1km-Kz5bJmNlz~N]M±^ qśO9;u _^6^7+ks[?qP}S. smmu_[+Oy%>CӾ}wzqqkhs ~_ >tT~efD9VZ_vEwW7/3a}g'e5E{W]W_XA]Ҩn[܃ˁxN1=X}pP0Zk}_M2zJu 4캗}kWYf eL9SX߮Rpo,E=v5ȢسŏP?]Һa;ӵ;c^{G}suװ_(=gY^= "J};_g|ge1ʦX6he}mWWK`}g|b0;*YAxƹǷs>4۳qWA,W;&ݏvA Z{=/Mz_Տ:+9I)]s3l?Xp 6ҳڱi~5,o~f}xk܌m~s ۉֽΫ:w?!_=mOo=?Sg:Sótme[vq7m$>u]Ya}R:}9/ί:NEW$c%R ?w_K?1cBߍqzgX3]*fC;(sEm[U}{}jS>dca]!3=^oyeMQ W޷pZWF}YmgsW6m{Y[##MٷQF8ӏ[豵6?פ}IrGXlژN-- X9rҞy}wEB -ȟsu(+adt7 y6Z%r5~Qe ԯ{2\=l3s/KK?ZJwRI$I%)$IJI$RI$I%)$IJI$RI$I%?TI%)$IJI$RI$I%)$IJI$RI$I%9Y:=[FﳲkgX^k 7_tzoڮ˗g7ǻ.|6ZcExRU\{A sS}/g_IJUcT[̐Ƌ\ѷs= $$I)^2͋?.g{}O6ڭie5p=jw阘˪=6mnjJtOJ!lZSU>iV5k;9)Ol?w:+9K0nNLȮv]UXVƽoNBN~59u5]ƇZk\\ݵ?Bwc=Lvٿk7xx;2h|o0w7uv1\7$ğ)3 Oz'|.z}kQMul֩tԶ~ELn߳ko)ħ%r/\_?_y]N>N589]^YSZZԳzwO s˩ﭶ48Z$̿_/?[TǯX=+ǩzVoٽT3zE~6]n}5KcsY^n/]b.&& ælvNihc]an;r2JkdtΝVVN-7d㙢+kYwsw`ecSn-ປ sK\6 +,rJ|٧׻C,q|;s>[z*:7LM lf08q9Z]IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$TI%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$I%)$IJI$RI$Tʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$ꤗʩ$8BIM!UAdobe PhotoshopAdobe Photoshop CS48BIM"vMM*bj(1er2ׇi-'-'Adobe Photoshop CS4 (11.0x20080609 [20080609.m.379 2008/06/09:02:00:00 cutoff; m branch]) Macintosh2008:05:13 14:35:59Xfn(vHH8BIMmaniIRFR8BIMAnDsnullAFStlongFrInVlLsObjcnullFrIDlong2"FrDllongFStsVlLsObjcnullFsIDlongAFrmlongFsFrVlLslong2"LCntlong8BIMRoll8BIMmfri8P8BIMMt328BIMLr327g***8BIMnorm%|(YARA8BIMTySh$<??@v@sp2TxLrTxt TEXTYARA textGriddingenum textGriddingNoneOrntenumOrntHrznAntAenumAnntAnSm TextIndexlong EngineDatatdta"x << /EngineDict << /Editor << /Text (YARA ) >> /ParagraphRun << /DefaultRunData << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> /RunArray [ << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> ] /RunLengthArray [ 5 ] /IsJoinable 1 >> /StyleRun << /DefaultRunData << /StyleSheet << /StyleSheetData << >> >> >> /RunArray [ << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading .04167 /HorizontalScale 1.2 /VerticalScale .9 /Tracking 60 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> ] /RunLengthArray [ 5 ] /IsJoinable 2 >> /GridInfo << /GridIsOn false /ShowGrid false /GridSize 18.0 /GridLeading 22.0 /GridColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /GridLeadingFillColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /AlignLineHeightToGridFlags false >> /AntiAlias 3 /UseFractionalGlyphWidths true /Rendered << /Version 1 /Shapes << /WritingDirection 0 /Children [ << /ShapeType 0 /Procession 0 /Lines << /WritingDirection 0 /Children [ ] >> /Cookie << /Photoshop << /ShapeType 0 /PointBase [ 0.0 0.0 ] /Base << /ShapeType 0 /TransformPoint0 [ 1.0 0.0 ] /TransformPoint1 [ 0.0 1.0 ] /TransformPoint2 [ 0.0 0.0 ] >> >> >> >> ] >> >> >> /ResourceDict << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 1 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (Krungthep) /Script 0 /FontType 1 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> /DocumentResources << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 1 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (Krungthep) /Script 0 /FontType 1 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> >>warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrzn8BIMluni YARA8BIMlnsrrend8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA c>dE8BIMfxrpm@Qr8!!!8BIMnorm)D(*/8BIMTySh( ??@z@s2TxLrTxt TEXT*/ textGriddingenum textGriddingNoneOrntenumOrntHrznAntAenumAnntAnSm TextIndexlong EngineDatatdta&L << /EngineDict << /Editor << /Text (*/ ) >> /ParagraphRun << /DefaultRunData << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> /RunArray [ << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> ] /RunLengthArray [ 3 ] /IsJoinable 1 >> /StyleRun << /DefaultRunData << /StyleSheet << /StyleSheetData << >> >> >> /RunArray [ << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading 50.0 /HorizontalScale 1.2 /VerticalScale .9 /Tracking -150 /AutoKerning false /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading 50.0 /HorizontalScale 1.2 /VerticalScale .9 /Tracking -150 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> ] /RunLengthArray [ 1 2 ] /IsJoinable 2 >> /GridInfo << /GridIsOn false /ShowGrid false /GridSize 18.0 /GridLeading 22.0 /GridColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /GridLeadingFillColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /AlignLineHeightToGridFlags false >> /AntiAlias 3 /UseFractionalGlyphWidths true /Rendered << /Version 1 /Shapes << /WritingDirection 0 /Children [ << /ShapeType 0 /Procession 0 /Lines << /WritingDirection 0 /Children [ ] >> /Cookie << /Photoshop << /ShapeType 0 /PointBase [ 0.0 0.0 ] /Base << /ShapeType 0 /TransformPoint0 [ 1.0 0.0 ] /TransformPoint1 [ 0.0 1.0 ] /TransformPoint2 [ 0.0 0.0 ] >> >> >> >> ] >> >> >> /ResourceDict << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 1 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (Krungthep) /Script 0 /FontType 1 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> /DocumentResources << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 1 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (Krungthep) /Script 0 /FontType 1 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> >>warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrzn8BIMluni*/8BIMlnsrrend8BIMlyid 8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA cׅD8BIMfxrp@$8!!!8BIMnorm)D(/*8BIMTySh( ??@j@s2TxLrTxt TEXT/* textGriddingenum textGriddingNoneOrntenumOrntHrznAntAenumAnntAnSm TextIndexlong EngineDatatdta&L << /EngineDict << /Editor << /Text (/* ) >> /ParagraphRun << /DefaultRunData << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> /RunArray [ << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> ] /RunLengthArray [ 3 ] /IsJoinable 1 >> /StyleRun << /DefaultRunData << /StyleSheet << /StyleSheetData << >> >> >> /RunArray [ << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading 50.0 /HorizontalScale 1.0 /VerticalScale .9 /Tracking -150 /AutoKerning false /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading 50.0 /HorizontalScale 1.0 /VerticalScale .9 /Tracking -150 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> ] /RunLengthArray [ 1 2 ] /IsJoinable 2 >> /GridInfo << /GridIsOn false /ShowGrid false /GridSize 18.0 /GridLeading 22.0 /GridColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /GridLeadingFillColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /AlignLineHeightToGridFlags false >> /AntiAlias 3 /UseFractionalGlyphWidths true /Rendered << /Version 1 /Shapes << /WritingDirection 0 /Children [ << /ShapeType 0 /Procession 0 /Lines << /WritingDirection 0 /Children [ ] >> /Cookie << /Photoshop << /ShapeType 0 /PointBase [ 0.0 0.0 ] /Base << /ShapeType 0 /TransformPoint0 [ 1.0 0.0 ] /TransformPoint1 [ 0.0 1.0 ] /TransformPoint2 [ 0.0 0.0 ] >> >> >> >> ] >> >> >> /ResourceDict << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 1 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (Krungthep) /Script 0 /FontType 1 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> /DocumentResources << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 1 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (Krungthep) /Script 0 /FontType 1 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> >>warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrzn8BIMluni/*8BIMlnsrrend8BIMlyid 8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA c6=l8BIMfxrp@^@"-$2338BIMnorm)({8BIMTySh(??@s@m 2TxLrTxt TEXT{ textGriddingenum textGriddingNoneOrntenumOrntHrznAntAenumAnntAnSm TextIndexlong EngineDatatdta& << /EngineDict << /Editor << /Text ({ ) >> /ParagraphRun << /DefaultRunData << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> /RunArray [ << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> ] /RunLengthArray [ 2 ] /IsJoinable 1 >> /StyleRun << /DefaultRunData << /StyleSheet << /StyleSheetData << >> >> >> /RunArray [ << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 200.0 /FauxBold true /FauxItalic false /AutoLeading true /Leading 150.0 /HorizontalScale 1.0 /VerticalScale 1.2 /Tracking 0 /AutoKerning false /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 .54902 .00862 .00867 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 200.0 /FauxBold true /FauxItalic false /AutoLeading true /Leading 150.0 /HorizontalScale 1.0 /VerticalScale 1.2 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 .54902 .00862 .00867 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> ] /RunLengthArray [ 1 1 ] /IsJoinable 2 >> /GridInfo << /GridIsOn false /ShowGrid false /GridSize 18.0 /GridLeading 22.0 /GridColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /GridLeadingFillColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /AlignLineHeightToGridFlags false >> /AntiAlias 3 /UseFractionalGlyphWidths true /Rendered << /Version 1 /Shapes << /WritingDirection 0 /Children [ << /ShapeType 0 /Procession 0 /Lines << /WritingDirection 0 /Children [ ] >> /Cookie << /Photoshop << /ShapeType 0 /PointBase [ 0.0 0.0 ] /Base << /ShapeType 0 /TransformPoint0 [ 1.0 0.0 ] /TransformPoint1 [ 0.0 1.0 ] /TransformPoint2 [ 0.0 0.0 ] >> >> >> >> ] >> >> >> /ResourceDict << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 2 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 2 >> << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> /DocumentResources << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 2 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 2 >> << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> >>warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrzn8BIMluni{8BIMlnsrrend8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA c0b8BIMfxrp@o@@IM# ++8BIMnorm)(}8BIMTySh(??@z0@p2TxLrTxt TEXT} textGriddingenum textGriddingNoneOrntenumOrntHrznAntAenumAnntAnSm TextIndexlong EngineDatatdta& << /EngineDict << /Editor << /Text (} ) >> /ParagraphRun << /DefaultRunData << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> /RunArray [ << /ParagraphSheet << /DefaultStyleSheet 0 /Properties << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> /Adjustments << /Axis [ 1.0 0.0 1.0 ] /XY [ 0.0 0.0 ] >> >> ] /RunLengthArray [ 2 ] /IsJoinable 1 >> /StyleRun << /DefaultRunData << /StyleSheet << /StyleSheetData << >> >> >> /RunArray [ << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 200.0 /FauxBold true /FauxItalic false /AutoLeading true /Leading 240.00002 /HorizontalScale 1.0 /VerticalScale 1.2 /Tracking 0 /AutoKerning false /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 .54902 .00862 .00867 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> << /StyleSheet << /StyleSheetData << /Font 0 /FontSize 200.0 /FauxBold true /FauxItalic false /AutoLeading true /Leading 240.00002 /HorizontalScale 1.0 /VerticalScale 1.2 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 .54902 .00862 .00867 ] >> /StrokeColor << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> /FillFlag true /StrokeFlag false /FillFirst false /YUnderline 1 /OutlineWidth .57884 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> >> ] /RunLengthArray [ 1 1 ] /IsJoinable 2 >> /GridInfo << /GridIsOn false /ShowGrid false /GridSize 18.0 /GridLeading 22.0 /GridColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /GridLeadingFillColor << /Type 1 /Values [ 0.0 0.0 0.0 1.0 ] >> /AlignLineHeightToGridFlags false >> /AntiAlias 3 /UseFractionalGlyphWidths true /Rendered << /Version 1 /Shapes << /WritingDirection 0 /Children [ << /ShapeType 0 /Procession 0 /Lines << /WritingDirection 0 /Children [ ] >> /Cookie << /Photoshop << /ShapeType 0 /PointBase [ 0.0 0.0 ] /Base << /ShapeType 0 /TransformPoint0 [ 1.0 0.0 ] /TransformPoint1 [ 0.0 1.0 ] /TransformPoint2 [ 0.0 0.0 ] >> >> >> >> ] >> >> >> /ResourceDict << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 2 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 2 >> << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> /DocumentResources << /KinsokuSet [ << /Name (PhotoshopKinsokuHard) /NoStart (00 00    0=]0 0 0 00000000A0C0E0G0I0c000000000000000000?!\)]},.:;!!  0) /NoEnd (  0;[00 0 00\([{ 0) /Keep (  %) /Hanging (00.,) >> << /Name (PhotoshopKinsokuSoft) /NoStart (00 0   0=]0 0 0 0000000) /NoEnd (  0;[00 0 00) /Keep (  %) /Hanging (00.,) >> ] /MojiKumiSet [ << /InternalName (Photoshop6MojiKumiSet1) >> << /InternalName (Photoshop6MojiKumiSet2) >> << /InternalName (Photoshop6MojiKumiSet3) >> << /InternalName (Photoshop6MojiKumiSet4) >> ] /TheNormalStyleSheet 0 /TheNormalParagraphSheet 0 /ParagraphSheetSet [ << /Name (Normal RGB) /DefaultStyleSheet 0 /Properties << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /AutoLeading 1.2 /LeadingType 0 /Hanging false /Burasagari false /KinsokuOrder 0 /EveryLineComposer false >> >> ] /StyleSheetSet [ << /Name (Normal RGB) /StyleSheetData << /Font 2 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /AutoKerning true /Kerning 0 /BaselineShift 0.0 /FontCaps 0 /FontBaseline 0 /Underline false /Strikethrough false /Ligatures true /DLigatures false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /NoBreak false /FillColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /StrokeColor << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> /FillFlag true /StrokeFlag false /FillFirst true /YUnderline 1 /OutlineWidth 1.0 /CharacterDirection 0 /HindiNumbers false /Kashida 1 /DiacriticPos 2 >> >> ] /FontSet [ << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 2 >> << /Name (OCRAStd) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (MyriadPro-Regular) /Script 0 /FontType 0 /Synthetic 0 >> << /Name (AdobeInvisFont) /Script 0 /FontType 0 /Synthetic 0 >> ] /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 >> >>warp warpStyleenum warpStylewarpNone warpValuedoubwarpPerspectivedoubwarpPerspectiveOtherdoub warpRotateenumOrntHrzn8BIMluni}8BIMlnsrrend8BIMlyid8BIMclbl8BIMinfx8BIMknko8BIMlspf8BIMlclr8BIMshmdH8BIMcust4metadata layerTimedoubA cԾnv8BIMfxrpS@OH́0 V@ h (!%PJ@ hRB*쀣q{[rЇl ;qp" F& a#Zn<2+\o؝x ̶^,mCFc\@XWq[Q A=: ]Ce<.T7A6D!9sRm. 2|Rvin), .ıcȸ_ UGhYrdaQZ;SpaDAtb}ю_! e?/.}Ue \rr5H2bN#B"1zNCS`3]\PQGa G܇r!RA'2C Uxap!,\&36{9΅%¹r.{?;FagַF䉏40/a>)1.hYhs15f_ep~r/+Z<<\di< ʔ0,҅0 GMuݭBthO®#"9 悽D QcU%FO މ9\ '΋|#a.p. m`XI>=/$,"V7h9=!4vϽӼ{g?ʻe'j#js1z'*sB1`^΅iR95ʹp.,9΅%¹\8 ’s\Xr. K΅saɹp.b`5H1 Om_>`<H1 Om_>`<H1 Om_>`<H̗q0ET@ *P%PBJ(\7(hбaqތo-\l "|WMkYwqL47I1+찵V`a+Tպ ةٱXJ+ B_^1ݷq8;HƃNϨu3uՈ$!^kmw0ҪդA ':&XetU}è'nhe[&Lmk;کH%j%`kTLeDErO0Gzc1F}6LltQU95FtrT꘭Eew>3ItZiA5jŦ߱%̂Se&Ϡ/SȄ.GЗuJʳ?sN Wi`\%du*;l<ZB;mEA)kغqcgefn)N-KU: :iH1 Om ?xH1 Om ?xH1 Om ?xHėQ0EUH@!P +a$J@BH,;IG&iچ|: oDwqޠ.3=wy3'CNjXW64z2 VW8wP(X`eK|W/at)SCzB& hQ:ge \! хeoRfq$WHM'@zīF;~#$E2fDSr: }f/^Pr;..^kdͨ]bЉAtb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ='/x,~A(WJ -]Sg@?-:I^{PZtϫqЌ&$4JQt( '6R4N#]çW{(7@)*']q "E&x!mC*B-~Ӈ:/C*ڎ(yJSu%Oo\?tvng4.<PEi^mZ/_͈H10TYz&0p2 HOP=A5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 j 'HOP3f =A@z5 jNWvF)!!Ji^g.wP'@7`y_xW,܉fz iD(͠s4W"7J3 gb$"%6O೰S eC*88 ^#x qK'퀇^+,3rCa8N)W > %> X`eك7MTGxJ tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb: |^ sҐr8Io=~& )ͧipvrJ"O9<& )m'\($mt2Ń: N:1؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:؃NA'{Љ=tb:s2ߛ͟EQ yj 2;i3˚wjwS\Ɖ)q26xख़&Ks'C g,\+1+IW6SY'DXČ/xHDiN">lL!}r%IxSW6]Vf $Mp'5nMO~`u:(IK3ЋNJTD5#1-@Q@KĹQgԫjUzKwV{ Noo$DA4R5Ƀyj^\&4,,4v{WU?OH :F@Oe\' @7daryɟƎcKkկZ}#?Z|'Yz"'Yz" 9b@r*HUɑ0#Sa$GHH$($A$ & Pp$%2( EL(JdBQ2@d& %$a(! C IJHPb؄&E'&%7(O2lQ6L(&}M^=ǤcF1i옴Q4Yixshgyx&Jdm6AlM6P&(} `\*$WA&h \V! 4Mާ J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP1)PfM Ye֤@R 0E)LfQ YdE?5ULFU*QdT6DMQjAC(Ո jDe5"se>(ue>(ue>(ue>(@d&P%3* BIMJjPRQ`C~&E؄&%6A9(oQP$EA&) 2IQɭQILBf;vwO4ILBLbbAc v&܎KBIjHRD d&L$4a"  Mȹ 3eg&tLW$c g$c g$c g$S lj Im2HR &$$Ii2IRL&$(Ie2JRT&$4A=d0%)LfI }OM4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai J&(MP4Ai䏊={~3)*fRU$a&3 EI(BLbbX"eA7 lM6H&;$mId#& ʣ u+c.MXcuG=ƤMc&1i옴I4y>ɆIdäOa'x MInHr@($FA$ # MIhHBF@d&$3$ %P(IBI"Lp($@ HIBMl``*+"*+j J j J[HQVQ-,{sgoYX`}H7b7ϖ. @xHzk߇?qLp\uLf$*$HUr< &'D(q$H82Wʄ8(qL$hq(qMJM~48&&t0=ar&v,8>cr&&8bv;&_s//Vv܎u ,3(OO792 (īL3*Ϊ -晼-h-jYVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲ-& ,UeQfYPA&Y&7D U Ϣ&Sx2n(Q/&PG&'_?(>"u렔3wXy(qhMr3)@ q(qMPt&neEagQHx?(5ƣ$Lp7WMK3Ɏ~LPqM~dIIƑ=䲻$d5ag8"҄)&G,5W&ѯZ Ǿ~& $Kۑ,jWL>IRw܏dlaQj ehrC^cY֯.T, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲ,d♿H&q(EgZ&09A2$;nO69C2$;ϐl1Es>&5a"ejrxQ$?&v,3&OO& ;n2 &Ij.gL#&P2D3H@i #q&I =R&D$Q(D`2Dcr&&8bv;&_s//Vv܎u ,3(OO792 (īL3*Ϊ -晼-h-jYVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲ-& ,UeQfYPA&Y&7D U Ϣ&Sx2n(Q/&PG&'_?(>"u렔3wXy(qhMr3)@ q(qMPt&neEagQHx?(5ƣ$Lp7WMK3Ɏ~LPqM~dIIƑ=䲻$d5ag8"҄)&G,5W&ѯZ Ǿ~& $Kۑ,jWL>IRw܏dlaQj ehrC^cY֯.T, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲfYMPA&YVT, *h4j e5AͲ,d♿H&q(EgZ&09A2$;nO69C2$;ϐl1Es>&5a"ejrxQ$?&v,3&OO& ;n2 &Ij.gL#&P2D3H@i #q&I =R&D$Q(D`2D> >> >> << /Resource << /StreamTag /CoolTypeFont /Identifier << /Name (OCRAStd) /Type 0 /Synthetic 2 >> >> >> << /Resource << /StreamTag /CoolTypeFont /Identifier << /Name (OCRAStd) /Type 0 >> >> >> << /Resource << /StreamTag /CoolTypeFont /Identifier << /Name (MyriadPro-Regular) /Type 0 >> >> >> << /Resource << /StreamTag /CoolTypeFont /Identifier << /Name (AdobeInvisFont) /Type 0 >> >> >> ] >> /MojiKumiCodeToClassSet << /Resources [ << /Resource << /Name () /Members << /ClassMappings [ << /R (55) /C 1 >> << /R (77) /C 1 >> << /R (99) /C 1 >> << /R (;;) /C 1 >> << /R (==) /C 1 >> << /R (??) /C 1 >> << /R (AA) /C 1 >> << /R (CC) /C 1 >> << /R () /C 1 >> << /R (;;) /C 1 >> << /R ([[) /C 1 >> << /R (  ) /C 1 >> << /R (00) /C 1 >> << /R (0 0 ) /C 1 >> << /R (0 0 ) /C 1 >> << /R (00) /C 1 >> << /R (00) /C 1 >> << /R (00) /C 1 >> << /R (66) /C 2 >> << /R (88) /C 2 >> << /R (::) /C 2 >> << /R (<<) /C 2 >> << /R (>>) /C 2 >> << /R (@@) /C 2 >> << /R (BB) /C 2 >> << /R (DD) /C 2 >> << /R ( ) /C 2 >> << /R (==) /C 2 >> << /R (]]) /C 2 >> << /R (  ) /C 2 >> << /R (0 0 ) /C 2 >> << /R (0 0 ) /C 2 >> << /R (0 0 ) /C 2 >> << /R (00) /C 2 >> << /R (00) /C 2 >> << /R (00) /C 2 >> << /R () /C 3 >> << /R (^^) /C 3 >> << /R (0A0A) /C 3 >> << /R (0C0C) /C 3 >> << /R (0E0E) /C 3 >> << /R (0G0G) /C 3 >> << /R (0I0I) /C 3 >> << /R (0c0c) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R (00) /C 3 >> << /R () /C 4 >> << /R () /C 4 >> << /R () /C 5 >> << /R (00) /C 5 >> << /R (  ) /C 5 >> << /R () /C 6 >> << /R (00) /C 6 >> << /R ( ) /C 7 >> << /R (00) /C 7 >> << /R (  ) /C 8 >> << /R ( % &) /C 8 >> << /R () /C 9 >> << /R () /C 9 >> << /R () /C 9 >> << /R () /C 10 >> << /R () /C 10 >> << /R () /C 10 >> << /R ( 0 0) /C 10 >> << /R ( 2 4) /C 10 >> << /R (00) /C 11 >> << /R (0B0B) /C 12 >> << /R (0D0D) /C 12 >> << /R (0F0F) /C 12 >> << /R (0H0H) /C 12 >> << /R (0J0b) /C 12 >> << /R (0d0) /C 12 >> << /R (00) /C 12 >> << /R (00) /C 12 >> << /R (00) /C 12 >> << /R () /C 13 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (00) /C 14 >> << /R (2 2C) /C 14 >> << /R (22) /C 14 >> << /R (22) /C 14 >> << /R (33W) /C 14 >> << /R (3q3v) /C 14 >> << /R (33) /C 14 >> << /R (N) /C 14 >> << /R (09) /C 15 >> << /R (!~) /C 16 >> << /R () /C 16 >> << /R (  ) /C 16 >> << /R (  ) /C 16 >> ] >> >> >> ] /DisplayList [ << /Resource 0 >> ] >> /MojiKumiTableSet << /Resources [ << /Resource << /Name (Photoshop6MojiKumiSet4) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 17 ] /Data << /B << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 17 ] /Data << /B << /R [ .25 .25 .25 ] >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .75 .75 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 17 ] /Data << /B << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 17 ] /Data << /B << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 17 /Elements [ << /P [ 17 1 ] /Data << /A << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 18 /Elements [ << /P [ 18 1 ] /Data << /A << /R [ .5 .5 .5 ] >> >> >> ] >> ] >> >> /PredefinedTag 2 >> >> >> << /Resource << /Name (Photoshop6MojiKumiSet3) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 17 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .75 .75 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 17 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 17 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> ] >> >> /PredefinedTag 4 >> >> >> << /Resource << /Name (Photoshop6MojiKumiSet2) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .75 .75 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> ] >> >> /PredefinedTag 3 >> >> >> << /Resource << /Name (Photoshop6MojiKumiSet1) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> ] >> >> /PredefinedTag 1 >> >> >> << /Resource << /Name (YakumonoHankaku) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 2 >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> ] >> >> /PredefinedTag 1 >> >> >> << /Resource << /Name (GyomatsuYakumonoHankaku) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .75 .75 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> ] >> >> /PredefinedTag 3 >> >> >> << /Resource << /Name (GyomatsuYakumonoZenkaku) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 17 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .75 .75 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 17 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 17 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> ] >> >> /PredefinedTag 4 >> >> >> << /Resource << /Name (YakumonoZenkaku) /Members << /CodeToClass 0 /AutoTsume << /TsumeMappings [ << /Before -.5 /Code () >> << /Before -.5 /Code (;) >> << /Before -.5 /Code ([) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code ( ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0 ) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /Before -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (=) >> << /After -.5 /Code (]) >> << /After -.5 /Code ( ) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0 ) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code (0) >> << /After -.5 /Code () >> << /After -.5 /Code (0) >> << /After -.5 /Code ( ) >> << /After -.5 /Code (0) >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code () >> << /Before -.25 /After -.25 /Code (0) >> << /Before -.25 /After -.25 /Code ( ) >> ] >> /Table << /DataArray << /SparseArray [ << /Index 1 /Elements [ << /P [ 1 5 ] /Data << /A << /R [ .25 .25 .25 ] /P 1 >> >> >> ] >> << /Index 2 /Elements [ << /P [ 2 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 2 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 2 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 2 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 11 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 3 >> >> >> << /P [ 2 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 2 17 ] /Data << /B << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 3 /Elements [ << /P [ 3 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 3 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 3 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 3 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 3 15 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 3 16 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 4 /Elements [ << /P [ 4 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 4 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 4 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 4 15 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 4 16 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> ] >> << /Index 5 /Elements [ << /P [ 5 1 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 2 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 3 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 4 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 5 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 1 >> >> >> << /P [ 5 6 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 7 ] /Data << /B << /R [ .25 .25 .25 ] /P 1 >> >> >> << /P [ 5 8 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 9 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 10 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 11 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 12 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 13 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 14 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 15 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 16 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 5 17 ] /Data << /B << /R [ .25 .25 .25 ] >> >> >> ] >> << /Index 6 /Elements [ << /P [ 6 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 6 3 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 4 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 5 ] /Data << /B << /R [ 0.0 .75 .75 ] /P 1 >> >> >> << /P [ 6 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 6 8 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 9 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 10 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 11 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 12 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 13 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 14 ] /Data << /B << /R [ 0.0 .5 .5 ] >> >> >> << /P [ 6 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 4 >> >> >> << /P [ 6 17 ] /Data << /B << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 7 /Elements [ << /P [ 7 1 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 3 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 4 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 5 ] /Data << /B << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 7 7 ] /Data << /B << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 7 8 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 9 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 10 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 12 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 13 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 14 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 15 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 16 ] /Data << /B << /R [ 0.0 .5 .5 ] /P 2 >> >> >> << /P [ 7 17 ] /Data << /B << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 8 /Elements [ << /P [ 8 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 8 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 8 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 8 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 8 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 8 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 9 /Elements [ << /P [ 9 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 9 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 9 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 9 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 9 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 9 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 10 /Elements [ << /P [ 10 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 10 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 10 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 10 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 10 15 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 10 16 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 11 /Elements [ << /P [ 11 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 11 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 11 15 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> << /P [ 11 16 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 4 >> >> >> ] >> << /Index 12 /Elements [ << /P [ 12 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 12 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 12 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 12 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 12 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 12 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 13 /Elements [ << /P [ 13 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 13 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 13 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 13 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 13 15 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 13 16 ] /Data << /B << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> ] >> << /Index 14 /Elements [ << /P [ 14 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 14 3 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 14 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 14 8 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 9 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 10 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 12 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 13 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 14 ] /Data << /A << /R [ 0.0 0.0 1.0 ] >> >> >> << /P [ 14 15 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 14 16 ] /Data << /B << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 15 /Elements [ << /P [ 15 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 15 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 15 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 15 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 15 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 15 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 15 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 15 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 16 /Elements [ << /P [ 16 1 ] /Data << /A << /R [ 0.0 .5 .5 ] /P 3 >> >> >> << /P [ 16 3 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 4 ] /Data << /A << /R [ .125 .25 .25 ] /P 4 >> >> >> << /P [ 16 5 ] /Data << /A << /R [ 0.0 .25 .25 ] /P 1 >> >> >> << /P [ 16 7 ] /Data << /A << /R [ 0.0 0.0 0.0 ] /P 2 >> >> >> << /P [ 16 8 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 9 ] /Data << /A << /R [ 0.0 0.0 .5 ] /P 4 >> >> >> << /P [ 16 10 ] /Data << /A << /R [ 0.0 0.0 .5 ] >> >> >> << /P [ 16 12 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> << /P [ 16 14 ] /Data << /A << /R [ .125 .25 .5 ] /P 4 >> >> >> ] >> << /Index 17 /Elements [ << /P [ 17 1 ] /Data << /A << /R [ .5 .5 .5 ] >> >> >> ] >> << /Index 18 /Elements [ << /P [ 18 1 ] /Data << /A << /R [ .5 .5 .5 ] >> >> >> ] >> ] >> >> /PredefinedTag 2 >> >> >> ] /DisplayList [ << /Resource 0 >> << /Resource 1 >> << /Resource 2 >> << /Resource 3 >> << /Resource 4 >> << /Resource 5 >> << /Resource 6 >> << /Resource 7 >> ] >> /KinsokuSet << /Resources [ << /Resource << /Name (None) /Data << /NoStart () /NoEnd () /Keep () /Hanging () /PredefinedTag 0 >> >> >> << /Resource << /Name (PhotoshopKinsokuHard) /Data << /NoStart (!\),.:;?]}    0!! 0000 0 0 0000A0C0E0G0I0c000000000000000000000000 =]) /NoEnd (\([{  00 0 0000 ;[) /Keep (  % &) /Hanging (00 ) /PredefinedTag 1 >> >> >> << /Resource << /Name (PhotoshopKinsokuSoft) /Data << /NoStart (  0000 0 0 00000000 =]) /NoEnd (  00 0 000;[) /Keep (  % &) /Hanging (00 ) /PredefinedTag 2 >> >> >> << /Resource << /Name (Hard) /Data << /NoStart (!\),.:;?]}    0!! 0000 0 0 0000A0C0E0G0I0c000000000000000000000000 =]) /NoEnd (\([{  00 0 0000 ;[) /Keep (  % &) /Hanging (00 ) /PredefinedTag 1 >> >> >> << /Resource << /Name (Soft) /Data << /NoStart (  0000 0 0 00000000 =]) /NoEnd (  00 0 000;[) /Keep (  % &) /Hanging (00 ) /PredefinedTag 2 >> >> >> ] /DisplayList [ << /Resource 0 >> << /Resource 1 >> << /Resource 2 >> << /Resource 3 >> << /Resource 4 >> ] >> /StyleSheetSet << /Resources [ << /Resource << /Name (Normal RGB) /Features << /Font 3 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> >> >> ] /DisplayList [ << /Resource 0 >> ] >> /ParagraphSheetSet << /Resources [ << /Resource << /Name (Normal RGB) /Features << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 0 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 0 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> >> >> ] /DisplayList [ << /Resource 0 >> ] >> /TextFrameSet << /Resources [ << /Resource << /Bezier << /Points [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ] >> /Data << /Type 0 /LineOrientation 0 /TextOnPathTRange [ -1.0 -1.0 ] /RowGutter 0.0 /ColumnGutter 0.0 /FirstBaselineAlignment << /Flag 1 /Min 0.0 >> /PathData << /Spacing -1 >> >> >> >> << /Resource << /Bezier << /Points [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ] >> /Data << /Type 0 /LineOrientation 0 /TextOnPathTRange [ -1.0 -1.0 ] /RowGutter 0.0 /ColumnGutter 0.0 /FirstBaselineAlignment << /Flag 1 /Min 0.0 >> /PathData << /Spacing -1 >> >> >> >> << /Resource << /Bezier << /Points [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ] >> /Data << /Type 0 /LineOrientation 0 /TextOnPathTRange [ -1.0 -1.0 ] /RowGutter 0.0 /ColumnGutter 0.0 /FirstBaselineAlignment << /Flag 1 /Min 0.0 >> /PathData << /Spacing -1 >> >> >> >> << /Resource << /Bezier << /Points [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ] >> /Data << /Type 0 /LineOrientation 0 /TextOnPathTRange [ -1.0 -1.0 ] /RowGutter 0.0 /ColumnGutter 0.0 /FirstBaselineAlignment << /Flag 1 /Min 0.0 >> /PathData << /Spacing -1 >> >> >> >> << /Resource << /Bezier << /Points [ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ] >> /Data << /Type 0 /LineOrientation 0 /TextOnPathTRange [ -1.0 -1.0 ] /RowGutter 0.0 /ColumnGutter 0.0 /FirstBaselineAlignment << /Flag 1 /Min 0.0 >> /PathData << /Spacing -1 >> >> >> >> ] >> >> /DocumentObjects << /DocumentSettings << /HiddenGlyphFont << /AlternateGlyphFont 4 /WhitespaceCharacterMapping [ << /WhitespaceCharacter ( ) /AlternateCharacter (1) >> << /WhitespaceCharacter ( ) /AlternateCharacter (6) >> << /WhitespaceCharacter ( ) /AlternateCharacter (0) >> << /WhitespaceCharacter ( \)) /AlternateCharacter (5) >> << /WhitespaceCharacter () /AlternateCharacter (5) >> << /WhitespaceCharacter (0) /AlternateCharacter (1) >> << /WhitespaceCharacter () /AlternateCharacter (3) >> ] >> /NormalStyleSheet 0 /NormalParagraphSheet 0 /SuperscriptSize .583 /SuperscriptPosition .333 /SubscriptSize .583 /SubscriptPosition .333 /SmallCapSize .7 /UseSmartQuotes true /SmartQuoteSets [ << /Language 0 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 1 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 2 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 3 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 4 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 5 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 6 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( 9) /CloseSingleQuote ( :) >> << /Language 7 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 8 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 9 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 10 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 11 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 12 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 13 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 14 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 15 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 16 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 17 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 18 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 19 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 20 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 21 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 22 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 23 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 24 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 25 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( 9) /CloseSingleQuote ( :) >> << /Language 26 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 27 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 28 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 29 /OpenDoubleQuote (0) /CloseDoubleQuote (0) >> << /Language 30 /OpenDoubleQuote (0 ) /CloseDoubleQuote (0 ) >> << /Language 31 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 32 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 33 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 34 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 35 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 36 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 37 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 38 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 39 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote (<) /CloseSingleQuote (>) >> << /Language 40 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 41 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote (<) /CloseSingleQuote (>) >> << /Language 42 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 43 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> << /Language 44 /OpenDoubleQuote () /CloseDoubleQuote () /OpenSingleQuote ( 9) /CloseSingleQuote ( :) >> << /Language 45 /OpenDoubleQuote ( ) /CloseDoubleQuote ( ) /OpenSingleQuote ( ) /CloseSingleQuote ( ) >> ] >> /TextObjects [ << /Model << /Text (} ) /ParagraphRun << /RunArray [ << /RunData << /ParagraphSheet << /Name () /Features << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 1 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << /Font 0 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> /Parent 0 >> >> /Length 2 >> ] >> /StyleRun << /RunArray [ << /RunData << /StyleSheet << /Name () /Parent 0 /Features << /Font 2 /FontSize 200.0 /FauxBold true /FauxItalic false /AutoLeading true /Leading 240.00002 /HorizontalScale 1.0 /VerticalScale 1.2 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 0 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .54902 .00862 .00867 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst false /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth .57884 /MiterLimit 2.31535 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> >> >> /Length 2 >> ] >> /KernRun << /RunArray [ << /RunData << >> /Length 2 >> ] >> /AlternateGlyphRun << /RunArray [ << /RunData << >> /Length 2 >> ] >> /FirstKern 0 >> /View << /Frames [ << /Resource 2 >> ] /RenderedData << /RunArray [ << /RunData << /LineCount 1 >> /Length 2 >> ] >> /Strikes [ << /StreamTag /PathSelectGroupCharacter /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 0 /Children [ << /StreamTag /FrameStrike /Frame 2 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /LineStrike /Baseline 0.0 /Leading 240.00002 /EMHeight 200.0 /DHeight 150.09766 /SelectionAscent -200.88136 /SelectionDescent 57.35962 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -200.88136 0.0 57.35962 ] /ChildProcession 1 /Children [ << /StreamTag /Segment /Mapping << /CharacterCount 2 /GlyphCount 0 /WRValid false >> /FirstCharacterIndexInSegment 0 /Transform << /Origin [ -149.39575 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /GlyphStrike /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -200.88136 149.39575 57.35962 ] /Glyphs [ 94 1 ] /GlyphAdjustments << /Data [ << >> ] /RunLengths [ 2 ] >> /VisualBounds [ -149.39575 -200.88136 0.0 57.35962 ] /RenderedBounds [ -149.39575 -200.88136 0.0 57.35962 ] /Invalidation [ -149.39575 -200.88136 149.39575 57.35962 ] /ShadowStylesRun << /Data [ << /Index 0 /Font 1 /Scale [ 1.0 1.2 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> << /Index 1 /Font 1 /Scale [ 1.0 1.2 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> ] /RunLengths [ 1 1 ] >> /EndsInCR true /SelectionAscent -200.88136 /SelectionDescent 57.35962 /MainDir 0 >> ] >> ] >> ] >> ] >> ] >> ] >> ] >> /OpticalAlignment false >> << /Model << /Text (YARA ) /ParagraphRun << /RunArray [ << /RunData << /ParagraphSheet << /Name () /Features << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 1 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << /Font 3 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> /Parent 0 >> >> /Length 5 >> ] >> /StyleRun << /RunArray [ << /RunData << /StyleSheet << /Name () /Parent 0 /Features << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading .04167 /HorizontalScale 1.2 /VerticalScale .9 /Tracking 60 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 0 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst false /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth .57884 /MiterLimit 2.31535 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> >> >> /Length 5 >> ] >> /KernRun << /RunArray [ << /RunData << >> /Length 5 >> ] >> /AlternateGlyphRun << /RunArray [ << /RunData << >> /Length 5 >> ] >> >> /View << /Frames [ << /Resource 1 >> ] /RenderedData << /RunArray [ << /RunData << /LineCount 1 >> /Length 5 >> ] >> /Strikes [ << /StreamTag /PathSelectGroupCharacter /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 0 /Children [ << /StreamTag /FrameStrike /Frame 1 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /LineStrike /Baseline 0.0 /Leading 50.0 /EMHeight 41.66667 /DHeight 31.25 /SelectionAscent -30.00011 /SelectionDescent 15.26356 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -30.00011 0.0 15.26356 ] /ChildProcession 1 /Children [ << /StreamTag /Segment /Mapping << /CharacterCount 5 /GlyphCount 0 /WRValid false >> /FirstCharacterIndexInSegment 0 /Transform << /Origin [ -142.27942 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /GlyphStrike /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -30.00011 145.27942 15.26356 ] /Glyphs [ 174 150 167 150 3 ] /GlyphAdjustments << /Data [ << /BackFixed 3.0 >> ] /RunLengths [ 5 ] >> /VisualBounds [ -142.27942 -30.00011 3.0 15.26356 ] /RenderedBounds [ -142.27942 -30.00011 3.0 15.26356 ] /Invalidation [ -142.27942 -30.00011 26.99979 15.26356 ] /ShadowStylesRun << /Data [ << /Index 0 /Font 0 /Scale [ 1.2 .9 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> ] /RunLengths [ 5 ] >> /EndsInCR true /SelectionAscent -30.00011 /SelectionDescent 15.26356 /MainDir 0 >> ] >> ] >> ] >> ] >> ] >> ] >> ] >> /OpticalAlignment false >> << /Model << /Text (/* ) /ParagraphRun << /RunArray [ << /RunData << /ParagraphSheet << /Name () /Features << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 1 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << /Font 0 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> /Parent 0 >> >> /Length 3 >> ] >> /StyleRun << /RunArray [ << /RunData << /StyleSheet << /Name () /Parent 0 /Features << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading 50.0 /HorizontalScale 1.0 /VerticalScale .9 /Tracking -150 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 0 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst false /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth .57884 /MiterLimit 2.31535 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> >> >> /Length 3 >> ] >> /KernRun << /RunArray [ << /RunData << >> /Length 3 >> ] >> /AlternateGlyphRun << /RunArray [ << /RunData << >> /Length 3 >> ] >> /FirstKern 0 >> /View << /Frames [ << /Resource 3 >> ] /RenderedData << /RunArray [ << /RunData << /LineCount 1 >> /Length 3 >> ] >> /Strikes [ << /StreamTag /PathSelectGroupCharacter /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 0 /Children [ << /StreamTag /FrameStrike /Frame 3 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /LineStrike /Baseline 0.0 /Leading 50.0 /EMHeight 41.66667 /DHeight 31.25 /SelectionAscent -30.00011 /SelectionDescent 15.26356 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -30.00011 0.0 15.26356 ] /ChildProcession 1 /Children [ << /StreamTag /Segment /Mapping << /CharacterCount 3 /GlyphCount 0 /WRValid false >> /FirstCharacterIndexInSegment 0 /Transform << /Origin [ -42.38229 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /GlyphStrike /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -30.00011 36.13229 15.26356 ] /Glyphs [ 132 127 3 ] /GlyphAdjustments << /Data [ << /BackFixed -6.25 >> ] /RunLengths [ 3 ] >> /VisualBounds [ -42.38229 -31.27499 -3.29654 15.26356 ] /RenderedBounds [ -42.38229 -31.27499 -3.29654 15.26356 ] /Invalidation [ -42.38229 -31.27499 13.74982 15.26356 ] /ShadowStylesRun << /Data [ << /Index 0 /Font 0 /Scale [ 1.0 .9 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> << /Index 1 /Font 0 /Scale [ 1.0 .9 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> ] /RunLengths [ 1 2 ] >> /EndsInCR true /SelectionAscent -30.00011 /SelectionDescent 15.26356 /MainDir 0 >> ] >> ] >> ] >> ] >> ] >> ] >> ] >> /OpticalAlignment false >> << /Model << /Text (*/ ) /ParagraphRun << /RunArray [ << /RunData << /ParagraphSheet << /Name () /Features << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 1 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << /Font 0 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> /Parent 0 >> >> /Length 3 >> ] >> /StyleRun << /RunArray [ << /RunData << /StyleSheet << /Name () /Parent 0 /Features << /Font 0 /FontSize 41.66667 /FauxBold false /FauxItalic false /AutoLeading true /Leading 50.0 /HorizontalScale 1.2 /VerticalScale .9 /Tracking -150 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 0 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst false /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth .57884 /MiterLimit 2.31535 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> >> >> /Length 3 >> ] >> /KernRun << /RunArray [ << /RunData << >> /Length 3 >> ] >> /AlternateGlyphRun << /RunArray [ << /RunData << >> /Length 3 >> ] >> /FirstKern 0 >> /View << /Frames [ << /Resource 4 >> ] /RenderedData << /RunArray [ << /RunData << /LineCount 1 >> /Length 3 >> ] >> /Strikes [ << /StreamTag /PathSelectGroupCharacter /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 0 /Children [ << /StreamTag /FrameStrike /Frame 4 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /LineStrike /Baseline 0.0 /Leading 50.0 /EMHeight 41.66667 /DHeight 31.25 /SelectionAscent -30.00011 /SelectionDescent 15.26356 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -30.00011 0.0 15.26356 ] /ChildProcession 1 /Children [ << /StreamTag /Segment /Mapping << /CharacterCount 3 /GlyphCount 0 /WRValid false >> /FirstCharacterIndexInSegment 0 /Transform << /Origin [ -50.85876 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /GlyphStrike /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -30.00011 43.35876 15.26356 ] /Glyphs [ 127 132 3 ] /GlyphAdjustments << /Data [ << /BackFixed -7.5 >> ] /RunLengths [ 3 ] >> /VisualBounds [ -50.85876 -31.27499 -4.17938 15.26356 ] /RenderedBounds [ -50.85876 -31.27499 -4.17938 15.26356 ] /Invalidation [ -50.85876 -31.27499 16.49979 15.26356 ] /ShadowStylesRun << /Data [ << /Index 0 /Font 0 /Scale [ 1.2 .9 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> << /Index 1 /Font 0 /Scale [ 1.2 .9 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> ] /RunLengths [ 1 2 ] >> /EndsInCR true /SelectionAscent -30.00011 /SelectionDescent 15.26356 /MainDir 0 >> ] >> ] >> ] >> ] >> ] >> ] >> ] >> /OpticalAlignment false >> << /Model << /Text ({ ) /ParagraphRun << /RunArray [ << /RunData << /ParagraphSheet << /Name () /Features << /Justification 1 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 8 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 1 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << /Font 0 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> /Parent 0 >> >> /Length 2 >> ] >> /StyleRun << /RunArray [ << /RunData << /StyleSheet << /Name () /Parent 0 /Features << /Font 2 /FontSize 200.0 /FauxBold true /FauxItalic false /AutoLeading true /Leading 150.0 /HorizontalScale 1.0 /VerticalScale 1.2 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 0 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 1 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .54902 .00862 .00867 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 .74902 .74902 .74902 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst false /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth .57884 /MiterLimit 2.31535 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> >> >> /Length 2 >> ] >> /KernRun << /RunArray [ << /RunData << >> /Length 2 >> ] >> /AlternateGlyphRun << /RunArray [ << /RunData << >> /Length 2 >> ] >> /FirstKern 0 >> /View << /Frames [ << /Resource 0 >> ] /RenderedData << /RunArray [ << /RunData << /LineCount 1 >> /Length 2 >> ] >> /Strikes [ << /StreamTag /PathSelectGroupCharacter /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 0 /Children [ << /StreamTag /FrameStrike /Frame 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /RowColStrike /RowColIndex 0 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 2 /Children [ << /StreamTag /LineStrike /Baseline 0.0 /Leading 240.00002 /EMHeight 200.0 /DHeight 150.09766 /SelectionAscent -200.88136 /SelectionDescent 57.35962 /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -200.88136 0.0 57.35962 ] /ChildProcession 1 /Children [ << /StreamTag /Segment /Mapping << /CharacterCount 2 /GlyphCount 0 /WRValid false >> /FirstCharacterIndexInSegment 0 /Transform << /Origin [ -149.39575 0.0 ] >> /Bounds [ 0.0 0.0 0.0 0.0 ] /ChildProcession 1 /Children [ << /StreamTag /GlyphStrike /Transform << /Origin [ 0.0 0.0 ] >> /Bounds [ 0.0 -200.88136 149.39575 57.35962 ] /Glyphs [ 92 1 ] /GlyphAdjustments << /Data [ << >> ] /RunLengths [ 2 ] >> /VisualBounds [ -149.39575 -200.88136 0.0 57.35962 ] /RenderedBounds [ -149.39575 -200.88136 0.0 57.35962 ] /Invalidation [ -149.39575 -200.88136 149.39575 57.35962 ] /ShadowStylesRun << /Data [ << /Index 0 /Font 1 /Scale [ 1.0 1.2 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> << /Index 1 /Font 1 /Scale [ 1.0 1.2 ] /Orientation 0 /BaselineDirection 2 /BaselineShift 0.0 /KernType 0 /EmbeddingLevel 0 /ComplementaryFontIndex 0 >> ] /RunLengths [ 1 1 ] >> /EndsInCR true /SelectionAscent -200.88136 /SelectionDescent 57.35962 /MainDir 0 >> ] >> ] >> ] >> ] >> ] >> ] >> ] >> /OpticalAlignment false >> ] /OriginalNormalStyleFeatures << /Font 3 /FontSize 12.0 /FauxBold false /FauxItalic false /AutoLeading true /Leading 0.0 /HorizontalScale 1.0 /VerticalScale 1.0 /Tracking 0 /BaselineShift 0.0 /CharacterRotation 0.0 /AutoKern 1 /FontCaps 0 /FontBaseline 0 /FontOTPosition 0 /StrikethroughPosition 0 /UnderlinePosition 0 /UnderlineOffset 0.0 /Ligatures true /DiscretionaryLigatures false /ContextualLigatures false /AlternateLigatures false /OldStyle false /Fractions false /Ordinals false /Swash false /Titling false /ConnectionForms false /StylisticAlternates false /Ornaments false /FigureStyle 0 /ProportionalMetrics false /Kana false /Italics false /Ruby false /BaselineDirection 2 /Tsume 0.0 /StyleRunAlignment 2 /Language 0 /JapaneseAlternateFeature 0 /EnableWariChu false /WariChuLineCount 2 /WariChuLineGap 0 /WariChuSubLineAmount << /WariChuSubLineScale .5 >> /WariChuWidowAmount 2 /WariChuOrphanAmount 2 /WariChuJustification 7 /TCYUpDownAdjustment 0 /TCYLeftRightAdjustment 0 /LeftAki -1.0 /RightAki -1.0 /JiDori 0 /NoBreak false /FillColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /StrokeColor << /StreamTag /SimplePaint /Color << /Type 1 /Values [ 1.0 0.0 0.0 0.0 ] >> >> /Blend << /StreamTag /SimpleBlender >> /FillFlag true /StrokeFlag false /FillFirst true /FillOverPrint false /StrokeOverPrint false /LineCap 0 /LineJoin 0 /LineWidth 1.0 /MiterLimit 4.0 /LineDashOffset 0.0 /LineDashArray [ ] /Type1EncodingNames [ ] /Kashidas 0 /DirOverride 0 /DigitSet 0 /DiacVPos 4 /DiacXOffset 0.0 /DiacYOffset 0.0 /OverlapSwash false /JustificationAlternates false /StretchedAlternates false /FillVisibleFlag true /StrokeVisibleFlag true >> /OriginalNormalParagraphFeatures << /Justification 0 /FirstLineIndent 0.0 /StartIndent 0.0 /EndIndent 0.0 /SpaceBefore 0.0 /SpaceAfter 0.0 /DropCaps 1 /AutoLeading 1.2 /LeadingType 0 /AutoHyphenate true /HyphenatedWordSize 6 /PreHyphen 2 /PostHyphen 2 /ConsecutiveHyphens 0 /Zone 36.0 /HyphenateCapitalized true /HyphenationPreference .5 /WordSpacing [ .8 1.0 1.33 ] /LetterSpacing [ 0.0 0.0 0.0 ] /GlyphSpacing [ 1.0 1.0 1.0 ] /SingleWordJustification 6 /Hanging false /AutoTCY 0 /KeepTogether true /BurasagariType 0 /KinsokuOrder 0 /Kinsoku /nil /KurikaeshiMojiShori false /MojiKumiTable /nil /EveryLineComposer false /TabStops << >> /DefaultTabWidth 36.0 /DefaultStyle << >> /ParagraphDirection 0 /JustificationMethod 0 /ComposerEngine 0 >> >>8BIMFMsk 2????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?c##?T?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?T?q????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ???"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?c##????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##?)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?x????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?M}~????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??jZZ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??jZZ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?q???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??7?c##?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????x?[?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?M}~?c##?x???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????q?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??7?c##????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??T??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?jZZ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??T???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??jZZ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?x??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?x???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????x?"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????jZZ?"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?M}~??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????x?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????q???"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##?)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T???????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ?c##?c##?M}~?7?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q???????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q???"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ?"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q?????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0??????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7???????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??q???????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?M}~????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??x?????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##?????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??x????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ???????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##??????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0??????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q?????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????????????????????????????????????????????????????????????????????????????????jZZ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?[?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?0?)ij?)ij?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?M}~??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q?T?)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?x????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij????????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7?x?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??0?FFF?c##?c##?c##??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??7?c##?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?q???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##????????????????????????????????????????????????????????????????????????????????x? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??jZZ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[?????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??jZZ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????M}~? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?M}~???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????q?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????x?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?x???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????x? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?c##????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?)ij?)ij?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?FFF?T?q??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??M}~?jZZ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??M}~??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????[? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?jZZ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??x????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?x?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?7?T?x?????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??FFF????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?x??????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?x?????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0?????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q???????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0?????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?x?????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?FFF?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?"22?x??????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?M}~?"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??FFF????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?[?FFF?7?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?7?T?x?????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????c##? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[???)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?"22? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?FFF??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?c##?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?M}~????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?q????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?T?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????)ij? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?)ij???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??q???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????jZZ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????q?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????FFF? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????x?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7?x???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????T?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??FFF?q???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????[???)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?)ij?0?FFF?[?q???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p?@?@?@?p?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`?@?@?@?P???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????????????????????????????@?@?@?@?@?@?@?@?p????????`?@?@?@?@?@?@?@?P??????????????????p?@?@?@?@?@?@?@?@?@?@?@?`??????????????????`?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?P?????????????????????????`?@?@?@?@?@?@?@?@?@?@?@?p???????????????????????????????????????????????????????0>@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`???????????????????????????????????@????????>???????????????p?>@>>?`???????????????=>?@???????????????????`>>>@??p???????????????????????????????????????????????????>?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????????????????????????????????@????????>??????????????@=???????????????>?p????????????????=?@?????????????????????????????????????????????????? >???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@?????????>>???????????????????????@????????>????????????? >?????????????>@??????????????>? ?????????????????????@???????????????????????p>?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>??????????>>???????????????????????@????????>????????????`=>?@?@?@?=?0????????????>??????0>??????????????0=??@?@?@>=?`????????????????????@???????????????????????>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ?????p?????>>??????p?????????????????@????????>?????????????P??????p>>????????????>???????>=????????????>>?p??????P???????????????p??????@???????p???????????????p=?`??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????>>? ???>>???? >>?????????????????@????????>???????????>>???????? ????????????>????????p?@???????????? ???????>>?????????????0>?@????@????? >@=?`??????????????>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=??????@>? ?>>?? >?@????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>????????????P=>? ?p?@??`?=>@?p????????????`=?`???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p?????>>>>?????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>????????????==>>>?@???????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????`>>?`?????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>?????????????p?>>? ?????????????`=?p??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`?????????@>>?@???????????????????@????????>???????????>?????????@????????????>????????=?????????????@????????>????????????????0>>?P??????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????@>>?@???????????????????>>???????p>>???????????>?????????@????????????>????????0??????????????@????????>?????????????????@>>?`???????????????@=?p???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@?????????@>>?@??????????????????>?`????@>@?@???????????>?????????@????????????>???????@=>??????????????@????????>??????????????? >@>?@????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????`>>?`????????????????p>>@>>>????????????>>???????>????????????>?????>@>??????????????>???????>>????????????`?=>? ?p?????????@>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ???????>>? >>? >>?????????????????`>@>?p????????????>????????????=>?@???????????????>????????????>??0?`>=?P????????>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????????p>>? ??>>??? >>?p??????????????????? >=>?@??????????????>????????????=>?p????????????>????????????>>? ???@???p?>?????????? >????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=???????????0? ????>>????? ?0???????????????????????@?0>>?@?P?????????????????>>???????>????????????>?????>@>????????????>???????>>?????????????p>?P?????@??????0??????????>?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p???????????????>>??????????????????????????????>????????????????????>?????????@????????????>???????@=?P????????????@????????>????????????????????@???????????????? >????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????????????>>??????????????????????????????>????????????????????>?????????@????????????>????????@?@????????????@????????>????????????????????@???????????????p>?@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`????????????????? ??? ??????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????`???????????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????????p=?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@???????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>@????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>?????????????????????????????????????`=?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????????????????????>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????????????????????`=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>???????????????????????????????????>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p?????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>???????????????????????????????????@=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????@>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>@>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P?@@? ?????????????????????????????????? ?`F????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi>{>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O?@@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>B???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? =< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>B??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?p#P????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?0???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????1< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>B????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????1< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?1???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?Pi???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>G,H?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?Pi?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?Pi??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O?`F???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>>B?@@?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????p ?0???????????????????????????????????@?p ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????`@@>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>‚?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?P``????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????p ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????`@@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>F?p ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>BB??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?P``?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????>BB<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?p ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>F???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?P``??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????p >F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????P``>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????p >‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????????`@@>BB>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>{< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @? ???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi?@@?@@?1>B>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F>{>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>B????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?0????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>B???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????1< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?`F???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?1????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>{< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?p#P?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>?p#P????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?Pi???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????1< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>?????????????????????????????????????????????????????????????????????????????????P``<<<<<<<<<<<<<<<<<<<<<>F??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? < @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?Pi????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?P``?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P?0???????????????????>>O>O=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>G,H????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F? >O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?p#P???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<>F?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?0???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<?@?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<>F??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<>‚?p ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>????????????????????????????????????????????????????????????????????????????????`@@<<<<<<<<<<<<<<<<<<<<<<<<>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? < @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<=>??@?@?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>‚?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@???????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?`@@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>O< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@????????????????????????????????????????????????????????????????????????????????p <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?P``??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?0????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?P``?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>F?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????1< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????p =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????????>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????????????????????????????????????????????????`@@=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????????p =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????p =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?p ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @??????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`@@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?????????????????????????????????????????????????????????????????????????????????p <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>BB???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>???????????????????? ?`@@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<>??P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????`@@<<<<<<<<<<<<<<<<<<<<<<<=???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<?P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<=?p ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<>BB?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<?0?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?p ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<=??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>BB??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>B? ?p#P?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>G,H?p#P??????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>G,H?p#P?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @? ???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>{< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`F< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>{???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pi=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @? ???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? < @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?`F???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>O????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>B< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @?@@????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? < @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@>< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>G,H?p#P?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P?=< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @>G,H?p#P??????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P?1>G,H< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @< @=?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#P?0?>B>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>O>B? ?p#P?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0>BB>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>F<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p >‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`@@=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>BB<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????P``<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`@@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?`@@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????P``<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>BB?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>BB<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`@@=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p >‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚?p ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??`@@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0>BB>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??0?`@@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p?@?@?@?p?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`?@?@?@?P???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????????????????????????????@?@?@?@?@?@?@?@?p????????`?@?@?@?@?@?@?@?P??????????????????p?@?@?@?@?@?@?@?@?@?@?@?`??????????????????`?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?P?????????????????????????`?@?@?@?@?@?@?@?@?@?@?@?p???????????????????????????????????????????????????????0>@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`???????????????????????????????????@????????>???????????????p?>@>>?`???????????????=>?@???????????????????`>>>@??p???????????????????????????????????????????????????>?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????????????????????????????????@????????>??????????????@=???????????????>?p????????????????=?@?????????????????????????????????????????????????? >???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@?????????>>???????????????????????@????????>????????????? >?????????????>@??????????????>? ?????????????????????@???????????????????????p>?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>??????????>>???????????????????????@????????>????????????`=>?@?@?@?=?0????????????>??????0>??????????????0=??@?@?@>=?`????????????????????@???????????????????????>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ?????p?????>>??????p?????????????????@????????>?????????????P??????p>>????????????>???????>=????????????>>?p??????P???????????????p??????@???????p???????????????p=?`??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????>>? ???>>???? >>?????????????????@????????>???????????>>???????? ????????????>????????p?@???????????? ???????>>?????????????0>?@????@????? >@=?`??????????????>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=??????@>? ?>>?? >?@????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>????????????P=>? ?p?@??`?=>@?p????????????`=?`???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p?????>>>>?????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>????????????==>>>?@???????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????`>>?`?????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>?????????????p?>>? ?????????????`=?p??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`?????????@>>?@???????????????????@????????>???????????>?????????@????????????>????????=?????????????@????????>????????????????0>>?P??????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????@>>?@???????????????????>>???????p>>???????????>?????????@????????????>????????0??????????????@????????>?????????????????@>>?`???????????????@=?p???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@?????????@>>?@??????????????????>?`????@>@?@???????????>?????????@????????????>???????@=>??????????????@????????>??????????????? >@>?@????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????`>>?`????????????????p>>@>>>????????????>>???????>????????????>?????>@>??????????????>???????>>????????????`?=>? ?p?????????@>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ???????>>? >>? >>?????????????????`>@>?p????????????>????????????=>?@???????????????>????????????>??0?`>=?P????????>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????????p>>? ??>>??? >>?p??????????????????? >=>?@??????????????>????????????=>?p????????????>????????????>>? ???@???p?>?????????? >????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=???????????0? ????>>????? ?0???????????????????????@?0>>?@?P?????????????????>>???????>????????????>?????>@>????????????>???????>>?????????????p>?P?????@??????0??????????>?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p???????????????>>??????????????????????????????>????????????????????>?????????@????????????>???????@=?P????????????@????????>????????????????????@???????????????? >????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????????????>>??????????????????????????????>????????????????????>?????????@????????????>????????@?@????????????@????????>????????????????????@???????????????p>?@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`????????????????? ??? ??????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????`???????????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????????p=?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@???????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>@????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>?????????????????????????????????????`=?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????????????????????>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????????????????????`=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>???????????????????????????????????>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p?????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>???????????????????????????????????@=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????@>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>@>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#?@? ?????????????????????????????????? ?`G????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj>>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@>T<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`G???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?p#????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?Pj???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>T<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>G6?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?Pj?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?Pj??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T?`G???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>?@?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????p ?0???????????????????????????????????@?p ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????`@@>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>‚?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?P``????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????p ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????`@@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>F?p ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>BB??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?P``?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????>BB<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?p ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>F???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?P``??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????p >F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????P``>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????p >‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????`@@>BB>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>T<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj<<<<<<<<<<<<<<<<<<<<<<?`G??????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#=<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@>T<<<<<<<<<<<<<<<<<<<<<<<<? ???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj?@?@?>><<<<<<<<<<<<<<<<<<<<<<<<<<?`G???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G>>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`G?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?`G???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>T<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?p#?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?p#????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?Pj???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ??????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`G?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?????????????????????????????????????????????????????????????????????????????????P``<<<<<<<<<<<<<<<<<<<<<>F??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?Pj????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?P``?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#?0???????????????????> >T>T=<<<<<<<<<<<<<<<<<<<<<<<<<<<>G6????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G? >T<<<<<<<<<<<<<<<<<<<<<<<<<?p#???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<>F?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@=<<<<<<<<<<<<<<<<<<<<<<<?0???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<?@?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#=<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<>F??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<>T????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<>‚?p ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>T<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????`@@<<<<<<<<<<<<<<<<<<<<<<<<>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<=>??@?@?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#<<<<<<<<<<<<<<<<<<<<<<?`G???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>‚?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<?@???????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?`@@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>T<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????????????????????????????????????????????p <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?P``??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<?0????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?P``?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>F?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????p =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????>F<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????`@@=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????p =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????p =<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?p ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`@@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????p <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>BB???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????>‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>???????????????????? ?`@@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<>??P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????`@@<<<<<<<<<<<<<<<<<<<<<<<=???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????0<<<<<<<<<<<<<<<<<<<<<<<?P``???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<=?p ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<>BB?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<?0?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<?p ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<=??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>BB??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<>>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>? ?p#?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>G6?p#??????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>G6?p#?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`G???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`G<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????Pj=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`G???????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>T????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>G6?p#?????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#?=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>G6?p#??????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#?>G6<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p#?0?>>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>T>? ?p#?????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0>BB>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>F<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p >‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`@@=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>BB<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????P``<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?`@@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=?`@@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????P``<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<?0????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>BB?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>BB<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`@@=<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p >‚<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>‚?p ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>??`@@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0>BB>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>??0?`@@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p?@?@?@?p?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`?@?@?@?P???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????????????????????????????@?@?@?@?@?@?@?@?p????????`?@?@?@?@?@?@?@?P??????????????????p?@?@?@?@?@?@?@?@?@?@?@?`??????????????????`?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?P?????????????????????????`?@?@?@?@?@?@?@?@?@?@?@?p???????????????????????????????????????????????????????0>@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`???????????????????????????????????@????????>???????????????p?>@>>?`???????????????=>?@???????????????????`>>>@??p???????????????????????????????????????????????????>?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????????????????????????????????@????????>??????????????@=???????????????>?p????????????????=?@?????????????????????????????????????????????????? >???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@?????????>>???????????????????????@????????>????????????? >?????????????>@??????????????>? ?????????????????????@???????????????????????p>?@??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>??????????>>???????????????????????@????????>????????????`=>?@?@?@?=?0????????????>??????0>??????????????0=??@?@?@>=?`????????????????????@???????????????????????>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ?????p?????>>??????p?????????????????@????????>?????????????P??????p>>????????????>???????>=????????????>>?p??????P???????????????p??????@???????p???????????????p=?`??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????>>? ???>>???? >>?????????????????@????????>???????????>>???????? ????????????>????????p?@???????????? ???????>>?????????????0>?@????@????? >@=?`??????????????>???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=??????@>? ?>>?? >?@????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>????????????P=>? ?p?@??`?=>@?p????????????`=?`???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p?????>>>>?????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>????????????==>>>?@???????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????`>>?`?????????????????@????????>???????????>?????????@????????????>?????????@????????????@????????>?????????????p?>>? ?????????????`=?p??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`?????????@>>?@???????????????????@????????>???????????>?????????@????????????>????????=?????????????@????????>????????????????0>>?P??????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????@>>?@???????????????????>>???????p>>???????????>?????????@????????????>????????0??????????????@????????>?????????????????@>>?`???????????????@=?p???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@?????????@>>?@??????????????????>?`????@>@?@???????????>?????????@????????????>???????@=>??????????????@????????>??????????????? >@>?@????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????`>>?`????????????????p>>@>>>????????????>>???????>????????????>?????>@>??????????????>???????>>????????????`?=>? ?p?????????@>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ???????>>? >>? >>?????????????????`>@>?p????????????>????????????=>?@???????????????>????????????>??0?`>=?P????????>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????????p>>? ??>>??? >>?p??????????????????? >=>?@??????????????>????????????=>?p????????????>????????????>>? ???@???p?>?????????? >????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=???????????0? ????>>????? ?0???????????????????????@?0>>?@?P?????????????????>>???????>????????????>?????>@>????????????>???????>>?????????????p>?P?????@??????0??????????>?@????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p???????????????>>??????????????????????????????>????????????????????>?????????@????????????>???????@=?P????????????@????????>????????????????????@???????????????? >????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>????????????????>>??????????????????????????????>????????????????????>?????????@????????????>????????@?@????????????@????????>????????????????????@???????????????p>?@???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`????????????????? ??? ??????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????`???????????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>???????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????????p=?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@?@???????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????????>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>@????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>?????????????????????????????????????`=?`????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????`? ????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????????????????????>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>?????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>????????????????????????????????????`=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????p=??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>???????????????????????????????????>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=?p?????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>???????????????????????????????????@=?p????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>>??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ?`??????????????????????????????????????????????????????>????????????????????>?????????@????????????>?????????@????????????@????????>??????????????????????????????????@>?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>@>??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????>? ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????=>>?????????????????????????????????>>>@??P???????????????????????????????????????@>>?@????????????????????????????????????????????=?0??????????????????????????????????????????????? >@?p?????????????????????????????????????????????????>?p??????????????????????????????????????????????????>>????????????????????????????????????????????????????? >@?p?????????????????????????????????????????????????????==?`??????????????????????????????????????????????????????>?0????????????????????????????????????????????????????????>?????????????????????????????????????????????????????????@=?p?????????????????????????????????????????????????????????@???????????????????????????????????????????????????????????@=?p??????????????????????????????????????????????????????????@>???????????????????????????????????????????????????????????@?`???????????????????????????????????????????????????????????@>???????????????????????????????????????????????????????????? >???????????????????????????????????????????????????????????>? ???????????????????????????????????????????????????????????>@?@???????????????????????????????????????????????????????????`???????????????????????????????????????????????????????????>???????????????????????????????????????????????????????????P>??????????????????????????????????????????????????????????p>@>?????????????????????????????????????????????????????????p>@>????????????????????????????????????????????????????????@>>?????????????????????????????????????????????????????`? >>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????=>?????????????????????????????????>>=>??????????????????????>? ???????????????????????????????????????`? >>??????????????????????>????????????????????????????????????????????`>>??????????????????????????????????????????????????????????????????????@>@>??????????????????????>?????????????????????????????????????????????????p>>??????????????????????=?`??????????????????????????????????????????????????>>??????????????????????>????????????????????????????????????????????????????>>???????????????????????P?????????????????????????????????????????????????????>>??????????????????????>???????????????????????????????????????????????????????p>@>??????????????????????>????????????????????????????????????????????????????????P=>????????????????????????????????????????????????????????????????????????????????>????????????????????????????????????????????????????????????????????????????????>@>???????????????????????0??????????????????????????????????????????????????????????@>???????????????????????@??????????????????????????????????????????????????????????>>?????????????????????????????????????????????????????????????????????????????????? >??????????????????????????????????????????????????????????????????????????????????=>??????????????????????>???????????????????????????????????????????????????????????>>??????????????????????>???????????????????????????????????????????????????????????? >???????????????????????p???????????????????????????????????????????????????????????P>??????????????????????? ???????????????????????????????????????????????????????????>??????????????????????>???????????????????????????????????????????????????????????>@>??????????????????????? ??????????????????????????????????????????????????????????>>??????????????????????=?P?????????????????????????????????????????????????????????>>??????????????????????>@?P????????????????????????????????????????????????????????>>??????????????????????=? ????????????????????????????????????????????????????????>??????????????????????>??P?????????????????????????????????????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>?????????????????????????????????????????????>?????????????????????????????????????????????>??????????????????????? ?????????????????????>>???????????????????????@?????????????????????>>????????????????????????????????????????????>>??????????????????????>@??????????????????????>>?????????????????????????????????????????????>??????????????????????=?p??????????????????????`>??????????????????????? ???????????????????????@>??????????????????????? ????????????????????????>??????????????????????>?@????????????????????????>>??????????????????????>@>>>? ?`??????????????????????????>>??????????????????????>??P???????????????????????????????????????????????????`>??????????????????????=? ?????????????????????????????????????????????????????? >??????????????????????>@?P??????????????????????????????????????????????????????>>??????????????????????=?P???????????????????????????????????????????????????????p>??????????????????????? ????????????????????????????????????????????????????????>??????????????????????>????????????????????????????????????????????????????????>>??????????????????????? ????????????????????????????????????????????????????????0>???????????????????????p???????????????????????????????????????????????????????>>??????????????????????>???????????????????????????????????????????????????????? >??????????????????????>???????????????????????????????????????????????????????p>>?????????????????????????????????????????????????????????????????????????????>>?????????????????????????????????????????????????????????????????????????????@>???????????????????????@?????????????????????????????????????????????????????p=>???????????????????????0?????????????????????????????????????????????????????>>?????????????????????????????????????????????????????????????????????????????`=>??????????????????????????????????????????????????????????????????????????????>??????????????????????>???????????????????????????????????????????????????????>@>??????????????????????>????????????????????????????????????????????????????????@>???????????????????????P???????????????????????????????????????????????????????>>??????????????????????>????????????????????????????????????????????????????????0>??????????????????????=?`???????????????????????????????????????????????????????>>??????????????????????0>????????????????????????????????????????????????????????>??????????????????????@????????????????????????????????????????????????????????`>@??????????????????????P>??????????????????????????????????????????????????????>@??????????????????????>? ???????????????????????????????????????????????????????????????????????????>@=>????????????????????0?@?@?p????????????????????????????P??????????????????????>>>?@?????????????????????????=?@??????????????????????P>?p???????????????????????>?0??????????????????????>=?p???????????????????????????????????????????????P>???????????????????????@>???????????????????????? =?@??????????????????????`>?????????????????????????`>>???????????????????????p??????????????????????????p?0?>>>=??????????????????????>?0???????????????????????????????????????????????????`? >?`?????????????????????>>??????????????????????????????????????????????????????@>?@?????????????????????>=???????????????????????????????????????????????????????p>@??????????????????????>?0???????????????????????????????????????????????????????p>@???????????????????????>????????????????????????????????????????????????????????P>???????????????????????p???????????????????????????????????????????????????????>>???????????????????????????????????????????????????????????????????????????????`>??????????????????????=?p???????????????????????????????????????????????????????>@>??????????????????????>???????????????????????????????????????????????????????>>???????????????????????P??????????????????????????????????????????????????????? >??????????????????????>???????????????????????????????????????????????????????@>?????????????????????????????????????????????????????????????????????????????@>???????????????????????@?????????????????????????????????????????????????????@>??????????????????????>?p?????????????????????????????????????????????????????@>???????????????????????@??????????????????????????????????????????????????????@>??????????????????????>???????????????????????????????????????????????????????@>??????????????????????=?p???????????????????????????????????????????????????????>??????????????????????????????????????????????????????????????????????????????>>??????????????????????=?p???????????????????????????????????????????????????????=>??????????????????????????????????????????????????????????????????????????????? >???????????????????????`???????????????????????????????????????????????????????>>??????????????????????>????????????????????????????????????????????????????????>???????????????????????0??????????????????????????????????????????????????????? >??????????????????????=???????????????????????????????????????????????????????>??????????????????????>?????????????????????????????????????????????????????@>>??????????????????????? ?????????????????????????????@?@?@???????????????????>>>???????????????????????`?????????????????????????`>>@>??????????????????????>????????????????????????p>>??????????????????????>???????????????????????>@>??????????????????????????????????????????????>???????????????????????0??????????????????????p=>???????????????????????@??????????????????????>???????????????????????p?????????????????????>>????????????????????????????????????????????=>????????????????????????????????????????????p>??????????????????????>??????????????????????@>??????????????????????>??????????????????????@>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????`?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@? >=>??????????????????????>???????????????????????????????????????????????????????p?>??????????????????????>?????????????????????????????????????????????????????????P=>??????????????????????>@??????????????????????????????????????????????????????????P=>?????????????????????????????????????????????????????????????????????????????????0>?????????????????????????????????????????????????????????????????????????????????>>???????????????????????P???????????????????????????????????????????????????????????@>???????????????????????0???????????????????????????????????????????????????????????>>??????????????????????????????????????????????????????????????????????????????????>>??????????????????????>@????????????????????????????????????????????????????????????>???????????????????????p???????????????????????????????????????????????????????????@>??????????????????????????????????????????????????????????????????????????????????@>??????????????????????>???????????????????????????????????????????????????????????@>??????????????????????? ??????????????????????????????????????????????????????????@>??????????????????????>??????????????????????????????????????????????????????????@>????????????????????????????????????????????????????????????????????????????????@>???????????????????????P????????????????????????????????????????????????????????>??????????????????????>@?p??????????????????????????????????????????????????????>>??????????????????????>??????????????????????????????????????????????????????>>????????????????????????????????????????????????????????????????????????????@>??????????????????????? ???????????????????????????????????????????????????>>??????????????????????>??????????????????????????????????????????????????0>??????????????????????>?`???????????????????????????????????????????????P=>??????????????????????=??p????????????????????????????????????????????P=>??????????????????????=>?P????????????????????????????????????????p?>??????????????????????=>?? ?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@? >=>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??????????????????????>??@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?P??????????????????????>?`???????????????????????????????????????????????????????=? ??????????????????????????????????????????????????????????0?????????????????????????????????????????????????????????>???????????????????????????????????????????????????????????>>?p??????????????????????????????????????????????????????????>?????????????????????????????????????????????????????????????`???????????????????????????????????????????????????????????`>@????????????????????????????????????????????????????????????0>???????????????????????????????????????????????????????????>????????????????????????????????????????????????????????????>????????????????????????????????????????????????????????????@?@??????????????????????????????????????????????????????????>?@??????????????????????????????????????????????????????????`??????????????????????????????????????????????????????????>??????????????????????????????????????????????????????????@>????????????????????????????????????????????????????????p>>@???????????????????????????????????????????????????????>?`???????????????????????????????????????????????????????????????????????????????????????????????????????????? >?p??????????????????????????????????????????????????? ??????????????????????????????????????????????????? ?0???????????????????????????????????????????????p>=? ????????????????????????????????????????????? =>?`????????????????????????????????????????`?>>??@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?@?0?>>=>>>=>>>>>@>???p=?@???>?`???>>>>>>>>=>>>>>>>>>@=>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>@>>>>>>>>>>>>=>????P>???>????????>?????????@=>?P??????????????`?>????????????????????p? >>??`??????????????P>=?`???>?@??? ????????>?????????@>?p??????????????????>???????????????????????=>???????????????????p>>????@>???>?@???@????????>?????????@>?????????????????????? ????????????????????????P? ?????????????????????>>????=?`???>? ???@?@???@????????>?????????@>?p????????? >>>??p????????>?????????@>?@?????????>?????????p?>>>? ?????????p>>?????????0>???>=?@???@=????????>?????????@?????????>@=?`????????@?????????@? ????????p?@????????`=>@?????????=>????==?p???>????`? ?`>?@???@>?`? ????????>?????????@?@???????? >?????????????????@=????????>????????>? ????????@>??0>>????>?P?p>????? =?p??>>????`>?@???@>?`???>????????>?????????@?@????????>?????????????????@????????>????????>?????????@>@?p????`>=>????>??p????P=>?p???>>???p=???????`?`???`?`??????????????>?????????@?@????????>?????????????????@????????>????????>?????????@>?p??????p?0????? ???????`>? ?????`???>?0?????????????0>????????>?????????@?@????????>?????????????????@????????>????????>?????????@=>?`???????????????@>>????p=>???>>?@?????????@>????????>?????????@?@????????>?????????????????@????????p????????>?????????@>?@??????????? >@? ?????@??? >?`???????`>?`????????0=?`???????? ?@????????>?????????????????@>???????>????????>?????????@>?@????????? >>????p=>???>>?@???????????@>??????????0>>?P????????>?@????????>?????????????????@>?p??????? ????????>?????????@>?P?????????????0>?@???>? ???@>? ???????????????0>=?`??????????P?@?`??????????0?@????????@???????? ?????????????????`??????P????????????????? ????????@????????@>??p?????????????????`>=>????`>???>??????`>?@???@>?`?????>?P???????????????????? =?@????????????????????????????????????????????p?0>?????????????????????????@>??????`?>????>? ?p?????>@?@???>????`=?`??`>?@???@>?`??`=>?0???????????????p? >?@???????????????????????????????????????????????p?=?????????????????????????@?0???@>>????=>?`??>>????`=?p??>>>?@???@>>>>?`????????@>>@?@????????@???????? ?????????????????`??????P??????????@????????? ????????@????????@=?>@>????>??`???>>???p=?@???@?@?????????@????????>?????????????????@>?p????????>@????????>?????????@>????>????@?`????@???@?@?????????@????????>?????????????????@>????????>????????>?????????@>????=?`???>>???>>??>?@?????????@????????>?????????????????@????????>????????>?????????@>?????????@?@???0?@?????????@????????>?????????????????@????????>????????>?????????@=?p???>>???>?@?????????@????????>?????????????????@????????>????????>?????????@????? ? ???P?@?????????@????????>?????????????????@????????>????????>?????????@>?p???>>???>?@?????????@????????>?????????????????@????????>????????>?????????@? ????? ???`?@?????????@????????>?????????????????@????????>????????>?????????@>????p==?p????@?????????@????????>?????????????????@????????>????????>?????????@? ????????p=?@?????????@????????>?????????????????@????????>????????>?????????@>????p=?`??? ?@?????????@????????>?????????????????@????????>????????>?????????@?@???>>???>?@?????????@????????>?????????????????@????????>????????>?????????@>????`?P???0?@???>yara-4.1.3/libyara/000077500000000000000000000000001413423160300140735ustar00rootroot00000000000000yara-4.1.3/libyara/Makefile.am000066400000000000000000000112441413423160300161310ustar00rootroot00000000000000# ABI_VERSION is passed to libtool as --version-number $(ABI_VERSION). This is # not related to YARA's release version, this is used for tracking changes in # the ABI, not in the project as a whole. # # The three number mean [current]:[revision]:[age], and they should updated as # follow: # # 1. With every release increment "revision". # # 2. If any interfaces have been added, removed, or changed since the last # update, increment "current" and set "revision" to 0. # # 3. If the changes in the interface were backward compatible (i.e: only adding # new APIs) increment "age", or set it to 0 if otherwise. # # See https://autotools.io/libtool/version.html for more details. # ABI_VERSION = 8:0:0 # Rules for generating YARA modules from .proto files. For each .proto file # three files are generated: .c, .pb-c.c, and .pb-c.h. The .c file is generated # by protoc-gen-yara and the other two by protoc-gen-c. This is done only if # protoc is found, if not, files already in the repository are used instead # of generating fresh ones from the .proto file. # if PROTOC SUFFIXES = .proto .pb-c.c .pb-c.h .c .proto.pb-c.c: $(PROTOC) --c_out=$(builddir) $^ -Ipb/ -I=$(srcdir) .proto.pb-c.h: $(PROTOC) --c_out=$(builddir) $^ -Ipb/ -I=$(srcdir) .proto.c: $(PROTOC) --c_out=$(builddir) $^ -Ipb/ -I=$(srcdir) endif MODULES = modules/tests/tests.c MODULES += modules/elf/elf.c MODULES += modules/math/math.c MODULES += modules/time/time.c MODULES += modules/pe/pe.c MODULES += modules/pe/pe_utils.c if CUCKOO_MODULE MODULES += modules/cuckoo/cuckoo.c endif if MAGIC_MODULE MODULES += modules/magic/magic.c endif if HASH_MODULE MODULES += modules/hash/hash.c endif if DOTNET_MODULE MODULES += modules/dotnet/dotnet.c endif if MACHO_MODULE MODULES += modules/macho/macho.c endif if DEX_MODULE MODULES += modules/dex/dex.c endif if PB_TESTS_MODULE MODULES += modules/pb_tests/pb_tests.c MODULES += modules/pb_tests/pb_tests.pb-c.c endif # # Add your modules here: # # MODULES += modules/yourmodule.c # AM_YFLAGS=-d AM_CFLAGS=-Wall -Wno-deprecated-declarations \ -D_GNU_SOURCE \ -I$(srcdir)/include if GCOV MOSTLYCLEANFILES = {.,proc,modules/*}/*.gc{no,da,ov} AM_CFLAGS+=-O0 -g -ftest-coverage -fprofile-arcs else if DEBUG AM_CFLAGS+=-g endif if OPTIMIZATION AM_CFLAGS+=-O3 else AM_CFLAGS+=-O0 endif endif if ADDRESS_SANITIZER AM_CFLAGS+=-fsanitize=address endif if GCC AM_CFLAGS+=-fvisibility=hidden endif ACLOCAL_AMFLAGS=-I m4 include_HEADERS = include/yara.h yaraincludedir = $(includedir)/yara yarainclude_HEADERS = \ include/yara/ahocorasick.h \ include/yara/arena.h \ include/yara/atoms.h \ include/yara/base64.h \ include/yara/bitmask.h \ include/yara/compiler.h \ include/yara/error.h \ include/yara/exec.h \ include/yara/exefiles.h \ include/yara/filemap.h \ include/yara/hash.h \ include/yara/integers.h \ include/yara/libyara.h \ include/yara/limits.h \ include/yara/mem.h \ include/yara/modules.h \ include/yara/notebook.h \ include/yara/object.h \ include/yara/parser.h \ include/yara/proc.h \ include/yara/re.h \ include/yara/rules.h \ include/yara/scan.h \ include/yara/scanner.h \ include/yara/sizedstr.h \ include/yara/stack.h \ include/yara/stopwatch.h \ include/yara/stream.h \ include/yara/strutils.h \ include/yara/threading.h \ include/yara/types.h \ include/yara/utils.h noinst_HEADERS = \ crypto.h \ exception.h \ include/yara/dotnet.h \ include/yara/elf.h \ include/yara/endian.h \ include/yara/globals.h \ include/yara/hex_lexer.h \ include/yara/lexer.h \ include/yara/pe.h \ include/yara/pe_utils.h \ include/yara/re_lexer.h \ modules/module_list dist_noinst_DATA = pb/yara.proto lib_LTLIBRARIES = libyara.la libyara_la_LDFLAGS = -version-number $(ABI_VERSION) BUILT_SOURCES = \ lexer.c \ hex_lexer.c \ re_lexer.c \ grammar.c \ hex_grammar.c \ re_grammar.c libyara_la_SOURCES = \ $(MODULES) \ grammar.y \ ahocorasick.c \ arena.c \ atoms.c \ base64.c \ bitmask.c \ compiler.c \ endian.c \ exec.c \ exefiles.c \ filemap.c \ hash.c \ hex_grammar.y \ hex_lexer.l \ lexer.l \ libyara.c \ mem.c \ modules.c \ notebook.c \ object.c \ parser.c \ proc.c \ re.c \ re_grammar.y \ re_lexer.l \ rules.c \ scan.c \ scanner.c \ sizedstr.c \ stack.c \ stopwatch.c \ strutils.c \ stream.c \ threading.c if USE_WINDOWS_PROC libyara_la_SOURCES += proc/windows.c endif if USE_LINUX_PROC libyara_la_SOURCES += proc/linux.c endif if USE_FREEBSD_PROC libyara_la_SOURCES += proc/freebsd.c endif if USE_OPENBSD_PROC libyara_la_SOURCES += proc/openbsd.c endif if USE_MACH_PROC libyara_la_SOURCES += proc/mach.c endif if USE_NO_PROC libyara_la_SOURCES += proc/none.c endif pkgconfigdir = $(libdir)/pkgconfig nodist_pkgconfig_DATA = yara.pc yara-4.1.3/libyara/ahocorasick.c000066400000000000000000000622731413423160300165370ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include typedef struct _QUEUE_NODE { YR_AC_STATE* value; struct _QUEUE_NODE* previous; struct _QUEUE_NODE* next; } QUEUE_NODE; typedef struct _QUEUE { QUEUE_NODE* head; QUEUE_NODE* tail; } QUEUE; //////////////////////////////////////////////////////////////////////////////// // Pushes an automaton state into the tail of a queue. // // Args: // queue: Pointer to the queue. // state: Pointer to the state being pushed into the queue. // // Returns: // ERROR_SUCCESS // ERROR_INSUFFICIENT_MEMORY // static int _yr_ac_queue_push(QUEUE* queue, YR_AC_STATE* state) { QUEUE_NODE* pushed_node; pushed_node = (QUEUE_NODE*) yr_malloc(sizeof(QUEUE_NODE)); if (pushed_node == NULL) return ERROR_INSUFFICIENT_MEMORY; pushed_node->previous = queue->tail; pushed_node->next = NULL; pushed_node->value = state; if (queue->tail != NULL) queue->tail->next = pushed_node; else // queue is empty queue->head = pushed_node; queue->tail = pushed_node; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Pops an automaton state from the head of a queue. // // Args: // queue: Pointer to the queue. // // Returns: // Pointer to the poped state. // static YR_AC_STATE* _yr_ac_queue_pop(QUEUE* queue) { YR_AC_STATE* result; QUEUE_NODE* popped_node; if (queue->head == NULL) return NULL; popped_node = queue->head; queue->head = popped_node->next; if (queue->head) queue->head->previous = NULL; else // queue is empty queue->tail = NULL; result = popped_node->value; yr_free(popped_node); return result; } //////////////////////////////////////////////////////////////////////////////// // Checks if a queue is empty. // // Args: // queue: Pointer to the queue. // // Returns: // true if queue is empty, false otherwise. // static int _yr_ac_queue_is_empty(QUEUE* queue) { return queue->head == NULL; } //////////////////////////////////////////////////////////////////////////////// // Given an automaton state and an input symbol, returns the new state // after reading the input symbol. // // Args: // state: Pointer to automaton state. // input: Input symbol. // // Returns: // Pointer to the next automaton state. // static YR_AC_STATE* _yr_ac_next_state(YR_AC_STATE* state, uint8_t input) { YR_AC_STATE* next_state = state->first_child; while (next_state != NULL) { if (next_state->input == input) return next_state; next_state = next_state->siblings; } return NULL; } //////////////////////////////////////////////////////////////////////////////// // Creates a new automaton state, the automaton will transition from // the given state to the new state after reading the input symbol. // // Args: // state: Pointer to the origin state. // input: Input symbol. // // Returns: // YR_AC_STATE* pointer to the newly allocated state or NULL in case // of error. // static YR_AC_STATE* _yr_ac_state_create(YR_AC_STATE* state, uint8_t input) { YR_AC_STATE* new_state = (YR_AC_STATE*) yr_malloc(sizeof(YR_AC_STATE)); if (new_state == NULL) return NULL; new_state->input = input; new_state->depth = state->depth + 1; new_state->matches_ref = YR_ARENA_NULL_REF; new_state->failure = NULL; new_state->t_table_slot = 0; new_state->first_child = NULL; new_state->siblings = state->first_child; state->first_child = new_state; return new_state; } //////////////////////////////////////////////////////////////////////////////// // Destroys an automaton state. // static int _yr_ac_state_destroy(YR_AC_STATE* state) { YR_AC_STATE* child_state = state->first_child; while (child_state != NULL) { YR_AC_STATE* next_child_state = child_state->siblings; _yr_ac_state_destroy(child_state); child_state = next_child_state; } yr_free(state); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Create failure links for each automaton state. // // This function must be called after all the strings have been added to the // automaton with yr_ac_add_string. // static int _yr_ac_create_failure_links(YR_AC_AUTOMATON* automaton) { YR_AC_STATE* current_state; YR_AC_STATE* failure_state; YR_AC_STATE* temp_state; YR_AC_STATE* state; YR_AC_STATE* transition_state; YR_AC_STATE* root_state; YR_AC_MATCH* match; QUEUE queue; queue.head = NULL; queue.tail = NULL; root_state = automaton->root; // Set the failure link of root state to itself. root_state->failure = root_state; // Push root's children and set their failure link to root. state = root_state->first_child; while (state != NULL) { FAIL_ON_ERROR(_yr_ac_queue_push(&queue, state)); state->failure = root_state; state = state->siblings; } // Traverse the trie in BFS order calculating the failure link // for each state. while (!_yr_ac_queue_is_empty(&queue)) { current_state = _yr_ac_queue_pop(&queue); match = yr_arena_ref_to_ptr(automaton->arena, ¤t_state->matches_ref); if (match != NULL) { // Find the last match in the list of matches. while (match->next != NULL) match = match->next; if (match->backtrack > 0) match->next = yr_arena_ref_to_ptr( automaton->arena, &root_state->matches_ref); } else { // This state doesn't have any matches, its matches will be those // in the root state, if any. current_state->matches_ref = root_state->matches_ref; } // Iterate over all the states that the current state can transition to. transition_state = current_state->first_child; while (transition_state != NULL) { FAIL_ON_ERROR(_yr_ac_queue_push(&queue, transition_state)); failure_state = current_state->failure; while (1) { temp_state = _yr_ac_next_state(failure_state, transition_state->input); if (temp_state != NULL) { transition_state->failure = temp_state; if (YR_ARENA_IS_NULL_REF(transition_state->matches_ref)) { transition_state->matches_ref = temp_state->matches_ref; } else { match = yr_arena_ref_to_ptr( automaton->arena, &transition_state->matches_ref); assert(match != NULL); // Find the last match in the list of matches. while (match->next != NULL) match = match->next; match->next = yr_arena_ref_to_ptr( automaton->arena, &temp_state->matches_ref); } break; } else { if (failure_state == root_state) { transition_state->failure = root_state; break; } else { failure_state = failure_state->failure; } } } // while(1) transition_state = transition_state->siblings; } } // while(!__yr_ac_queue_is_empty(&queue)) return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Returns true if the transitions for state s2 are a subset of the transitions // for state s1. In other words, if at state s2 input X is accepted, it must be // accepted in s1 too. // static bool _yr_ac_transitions_subset(YR_AC_STATE* s1, YR_AC_STATE* s2) { uint8_t set[32]; YR_AC_STATE* state = s1->first_child; memset(set, 0, 32); while (state != NULL) { set[state->input / 8] |= 1 << state->input % 8; state = state->siblings; } state = s2->first_child; while (state != NULL) { if (!(set[state->input / 8] & 1 << state->input % 8)) return false; state = state->siblings; } return true; } //////////////////////////////////////////////////////////////////////////////// // Removes unnecessary failure links. // static int _yr_ac_optimize_failure_links(YR_AC_AUTOMATON* automaton) { QUEUE queue = {NULL, NULL}; // Push root's children. YR_AC_STATE* root_state = automaton->root; YR_AC_STATE* state = root_state->first_child; while (state != NULL) { FAIL_ON_ERROR(_yr_ac_queue_push(&queue, state)); state = state->siblings; } while (!_yr_ac_queue_is_empty(&queue)) { YR_AC_STATE* current_state = _yr_ac_queue_pop(&queue); if (current_state->failure != root_state) { if (_yr_ac_transitions_subset(current_state, current_state->failure)) current_state->failure = current_state->failure->failure; } // Push children of current_state state = current_state->first_child; while (state != NULL) { FAIL_ON_ERROR(_yr_ac_queue_push(&queue, state)); state = state->siblings; } } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Find a place within the automaton's transition table where the transitions // for the given state can be put. The function first create a bitmask for the // state's transition table, then searches for an offset within the automaton's // bitmask where the state's bitmask can be put without bit collisions. // static int _yr_ac_find_suitable_transition_table_slot( YR_AC_AUTOMATON* automaton, YR_ARENA* arena, YR_AC_STATE* state, uint32_t* slot) { // The state's transition table has 257 entries, 1 for the failure link and // 256 for each possible input byte, so the state's bitmask has 257 bits. YR_BITMASK state_bitmask[YR_BITMASK_SIZE(257)]; YR_AC_STATE* child_state = state->first_child; // Start with all bits set to zero. yr_bitmask_clear_all(state_bitmask); // The first slot in the transition table is for the state's failure link, // so the first bit in the bitmask must be set to one. yr_bitmask_set(state_bitmask, 0); while (child_state != NULL) { yr_bitmask_set(state_bitmask, child_state->input + 1); child_state = child_state->siblings; } *slot = yr_bitmask_find_non_colliding_offset( automaton->bitmask, state_bitmask, automaton->tables_size, 257, &automaton->t_table_unused_candidate); // Make sure that we are not going beyond the maximum size of the transition // table, starting at the slot found there must be at least 257 other slots // for accommodating the state's transition table. assert(*slot + 257 < YR_AC_MAX_TRANSITION_TABLE_SIZE); if (*slot > automaton->tables_size - 257) { FAIL_ON_ERROR(yr_arena_allocate_zeroed_memory( arena, YR_AC_TRANSITION_TABLE, 257 * sizeof(YR_AC_TRANSITION), NULL)); FAIL_ON_ERROR(yr_arena_allocate_zeroed_memory( arena, YR_AC_STATE_MATCHES_TABLE, 257 * sizeof(uint8_t*), NULL)); size_t bm_len = YR_BITMASK_SIZE(automaton->tables_size) * sizeof(YR_BITMASK); size_t bm_len_incr = YR_BITMASK_SIZE(257) * sizeof(YR_BITMASK); automaton->bitmask = yr_realloc(automaton->bitmask, bm_len + bm_len_incr); if (automaton->bitmask == NULL) return ERROR_INSUFFICIENT_MEMORY; memset((uint8_t*) automaton->bitmask + bm_len, 0, bm_len_incr); automaton->tables_size += 257; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Builds the transition table for the automaton. The transition table (T) is a // large array of 32-bits integers. Each state in the automaton is represented // by an index S within the array. The integer stored in T[S] is the failure // link for state S, it contains the index of the next state when no valid // transition exists for the next input byte. // // At position T[S+1+B] (where B is a byte) we can find the transition (if any) // that must be followed from state S if the next input is B. The value in // T[S+1+B] contains the index for next state or zero. A zero value means that // no valid transition exists from state S when next input is B, and the failure // link must be used instead. // // The transition table for state S starts at T[S] and spans the next 257 // slots in the array (1 for the failure link and 256 for all the possible // transitions). But many of those slots are for invalid transitions, so // the transitions for multiple states can be interleaved as long as they don't // collide. For example, instead of having this transition table with state S1 // and S2 separated by a large number of slots: // // S1 S2 // +------+------+------+------+-- ~ --+------+------+------+-- ~ --+ // | FLS1 | X | - | - | - | Y | FLS2 | Z | - | // +------+------+------+------+-- ~ --+------+------+------+-- ~ --+ // // We can interleave the transitions for states S1 and S2 and get this other // transition table, which is more compact: // // S1 S2 // +------+------+------+------+-- ~ --+------+ // | FLS1 | X | FLS2 | Z | - | Y | // +------+------+------+------+-- ~ --+------+ // // And how do we know that transition Z belongs to state S2 and not S1? Or that // transition Y belongs to S1 and not S2? Because each slot of the array not // only contains the index for the state where the transition points to, it // also contains the offset of the transition relative to its owner state. So, // the value for the owner offset would be 1 for transitions X, because X // belongs to state S1 and it's located 1 position away from S1. The same occurs // for Z, it belongs to S2 and it's located one position away from S2 so its // owner offset is 1. If we are in S1 and next byte is 2, we are going to read // the transition at T[S1+1+2] which is Z. But we know that transition Z is not // a valid transition for state S1 because the owner offset for Z is 1 not 3. // // Each 32-bit slot in the transition table has 23 bits for storing the index // of the target state and 9 bits for storing the offset of the slot relative // to its own state. The offset can be any value from 0 to 256, both inclusive, // hence 9 bits are required for it. The layout for the slot goes like: // // 32 23 0 // +-----------------------+---------+ // | Target state's index | Offset | // +-----------------------+---------+ // // A more detailed description can be found in: http://goo.gl/lE6zG // static int _yr_ac_build_transition_table(YR_AC_AUTOMATON* automaton) { YR_AC_TRANSITION* t_table; uint32_t* m_table; YR_AC_STATE* state; YR_AC_STATE* child_state; YR_AC_STATE* root_state = automaton->root; uint32_t slot; QUEUE queue = {NULL, NULL}; // Both t_table and m_table have 512 slots initially, which is enough for the // root node's transition table. automaton->tables_size = 512; automaton->bitmask = yr_calloc( YR_BITMASK_SIZE(automaton->tables_size), sizeof(YR_BITMASK)); if (automaton->bitmask == NULL) return ERROR_INSUFFICIENT_MEMORY; FAIL_ON_ERROR(yr_arena_allocate_zeroed_memory( automaton->arena, YR_AC_TRANSITION_TABLE, automaton->tables_size * sizeof(YR_AC_TRANSITION), NULL)); FAIL_ON_ERROR(yr_arena_allocate_zeroed_memory( automaton->arena, YR_AC_STATE_MATCHES_TABLE, automaton->tables_size * sizeof(uint32_t), NULL)); t_table = yr_arena_get_ptr(automaton->arena, YR_AC_TRANSITION_TABLE, 0); m_table = yr_arena_get_ptr(automaton->arena, YR_AC_STATE_MATCHES_TABLE, 0); // The failure link for the root node points to itself. t_table[0] = YR_AC_MAKE_TRANSITION(0, 0); // Initialize the entry corresponding to the root node in the match table. // Entries in this table are the index within YR_AC_MATCH_POOL where resides // the YR_AC_MATCH structure that corresponds to the head of the matches list // for the node. The indexes start counting at 1, the zero is used for // indicating that the node has no associated matches. if (!YR_ARENA_IS_NULL_REF(root_state->matches_ref)) m_table[0] = root_state->matches_ref.offset / sizeof(YR_AC_MATCH) + 1; // Mark the first slot in the transition table as used. yr_bitmask_set(automaton->bitmask, 0); // Index 0 is for root node. Unused indexes start at 1. automaton->t_table_unused_candidate = 1; child_state = root_state->first_child; while (child_state != NULL) { // Each state stores its slot number. child_state->t_table_slot = child_state->input + 1; t_table[child_state->input + 1] = YR_AC_MAKE_TRANSITION( 0, child_state->input + 1); yr_bitmask_set(automaton->bitmask, child_state->input + 1); FAIL_ON_ERROR(_yr_ac_queue_push(&queue, child_state)); child_state = child_state->siblings; } while (!_yr_ac_queue_is_empty(&queue)) { state = _yr_ac_queue_pop(&queue); FAIL_ON_ERROR(_yr_ac_find_suitable_transition_table_slot( automaton, automaton->arena, state, &slot)); // _yr_ac_find_suitable_transition_table_slot can allocate more space in // both tables and cause the tables to be moved to a different memory // location, we must get their up-to-date addresses. t_table = yr_arena_get_ptr(automaton->arena, YR_AC_TRANSITION_TABLE, 0); m_table = yr_arena_get_ptr(automaton->arena, YR_AC_STATE_MATCHES_TABLE, 0); t_table[state->t_table_slot] |= (slot << YR_AC_SLOT_OFFSET_BITS); t_table[slot] = YR_AC_MAKE_TRANSITION(state->failure->t_table_slot, 0); // The match table is an array of indexes within YR_AC_MATCHES_POOL. The // N-th item in the array is the index for the YR_AC_MATCH structure that // represents the head of the matches list for state N. The indexes start // at 1, the 0 indicates that there are no matches for the state. if (YR_ARENA_IS_NULL_REF(state->matches_ref)) m_table[slot] = 0; else m_table[slot] = state->matches_ref.offset / sizeof(YR_AC_MATCH) + 1; state->t_table_slot = slot; yr_bitmask_set(automaton->bitmask, slot); // Push children of current_state child_state = state->first_child; while (child_state != NULL) { child_state->t_table_slot = slot + child_state->input + 1; t_table[child_state->t_table_slot] = YR_AC_MAKE_TRANSITION( 0, child_state->input + 1); yr_bitmask_set(automaton->bitmask, child_state->t_table_slot); FAIL_ON_ERROR(_yr_ac_queue_push(&queue, child_state)); child_state = child_state->siblings; } } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Prints automaton state for debug purposes. This function is invoked by // yr_ac_print_automaton, is not intended to be used stand-alone. // static void _yr_ac_print_automaton_state( YR_AC_AUTOMATON* automaton, YR_AC_STATE* state) { int child_count; YR_AC_MATCH* match; YR_AC_STATE* child_state; for (int i = 0; i < state->depth; i++) printf(" "); child_state = state->first_child; child_count = 0; while (child_state != NULL) { child_count++; child_state = child_state->siblings; } printf( "%p childs:%d depth:%d failure:%p", state, child_count, state->depth, state->failure); match = yr_arena_ref_to_ptr(automaton->arena, &state->matches_ref); while (match != NULL) { printf("\n"); for (int i = 0; i < state->depth + 1; i++) printf(" "); printf("%s = ", match->string->identifier); if (STRING_IS_HEX(match->string)) { printf("{ "); for (int i = 0; i < yr_min(match->string->length, 10); i++) printf("%02x ", match->string->string[i]); printf("}"); } else if (STRING_IS_REGEXP(match->string)) { printf("/"); for (int i = 0; i < yr_min(match->string->length, 10); i++) printf("%c", match->string->string[i]); printf("/"); } else { printf("\""); for (int i = 0; i < yr_min(match->string->length, 10); i++) printf("%c", match->string->string[i]); printf("\""); } match = match->next; } printf("\n"); child_state = state->first_child; while (child_state != NULL) { _yr_ac_print_automaton_state(automaton, child_state); child_state = child_state->siblings; } } //////////////////////////////////////////////////////////////////////////////// // Creates a new automaton // int yr_ac_automaton_create(YR_ARENA* arena, YR_AC_AUTOMATON** automaton) { YR_AC_AUTOMATON* new_automaton; YR_AC_STATE* root_state; new_automaton = (YR_AC_AUTOMATON*) yr_malloc(sizeof(YR_AC_AUTOMATON)); root_state = (YR_AC_STATE*) yr_malloc(sizeof(YR_AC_STATE)); if (new_automaton == NULL || root_state == NULL) { yr_free(new_automaton); yr_free(root_state); return ERROR_INSUFFICIENT_MEMORY; } root_state->depth = 0; root_state->matches_ref = YR_ARENA_NULL_REF; root_state->failure = NULL; root_state->first_child = NULL; root_state->siblings = NULL; root_state->t_table_slot = 0; new_automaton->arena = arena; new_automaton->root = root_state; new_automaton->bitmask = NULL; new_automaton->tables_size = 0; *automaton = new_automaton; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Destroys automaton // int yr_ac_automaton_destroy(YR_AC_AUTOMATON* automaton) { _yr_ac_state_destroy(automaton->root); yr_free(automaton->bitmask); yr_free(automaton); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Adds a string to the automaton. This function is invoked once for each // string defined in the rules. // int yr_ac_add_string( YR_AC_AUTOMATON* automaton, YR_STRING* string, uint32_t string_idx, YR_ATOM_LIST_ITEM* atom, YR_ARENA* arena) { while (atom != NULL) { YR_AC_STATE* state = automaton->root; for (int i = 0; i < atom->atom.length; i++) { YR_AC_STATE* next_state = _yr_ac_next_state(state, atom->atom.bytes[i]); if (next_state == NULL) { next_state = _yr_ac_state_create(state, atom->atom.bytes[i]); if (next_state == NULL) return ERROR_INSUFFICIENT_MEMORY; } state = next_state; } YR_ARENA_REF new_match_ref; FAIL_ON_ERROR(yr_arena_allocate_struct( arena, YR_AC_STATE_MATCHES_POOL, sizeof(YR_AC_MATCH), &new_match_ref, offsetof(YR_AC_MATCH, string), offsetof(YR_AC_MATCH, forward_code), offsetof(YR_AC_MATCH, backward_code), offsetof(YR_AC_MATCH, next), EOL)); YR_AC_MATCH* new_match = yr_arena_ref_to_ptr(arena, &new_match_ref); new_match->backtrack = state->depth + atom->backtrack; new_match->string = yr_arena_get_ptr( arena, YR_STRINGS_TABLE, string_idx * sizeof(struct YR_STRING)); new_match->forward_code = yr_arena_ref_to_ptr( arena, &atom->forward_code_ref); new_match->backward_code = yr_arena_ref_to_ptr( arena, &atom->backward_code_ref); // Add newly created match to the list of matches for the state. new_match->next = yr_arena_ref_to_ptr(arena, &state->matches_ref); state->matches_ref = new_match_ref; atom = atom->next; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Compiles the Aho-Corasick automaton, the resulting data structures are // are written in the provided arena. // int yr_ac_compile(YR_AC_AUTOMATON* automaton, YR_ARENA* arena) { FAIL_ON_ERROR(_yr_ac_create_failure_links(automaton)); FAIL_ON_ERROR(_yr_ac_optimize_failure_links(automaton)); FAIL_ON_ERROR(_yr_ac_build_transition_table(automaton)); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Prints automaton for debug purposes. // void yr_ac_print_automaton(YR_AC_AUTOMATON* automaton) { printf("-------------------------------------------------------\n"); _yr_ac_print_automaton_state(automaton, automaton->root); printf("-------------------------------------------------------\n"); } yara-4.1.3/libyara/arena.c000066400000000000000000000463431413423160300153370ustar00rootroot00000000000000/* Copyright (c) 2020. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include typedef struct YR_ARENA_FILE_HEADER YR_ARENA_FILE_HEADER; typedef struct YR_ARENA_FILE_BUFFER YR_ARENA_FILE_BUFFER; #pragma pack(push) #pragma pack(1) struct YR_ARENA_FILE_HEADER { uint8_t magic[4]; uint8_t version; uint8_t num_buffers; }; struct YR_ARENA_FILE_BUFFER { uint64_t offset; uint32_t size; }; #pragma pack(pop) //////////////////////////////////////////////////////////////////////////////// // Tells the arena that certain offsets within a buffer contain relocatable // pointers. The offsets are passed as a vararg list where the end of the // list is indicated by the special value EOL (-1), offsets in the list are // relative to base_offset, which in turns is relative to the beginning of the // buffer. // // Args: // arena: Pointer the arena. // buffer_id: Buffer number. // base_offset: Base offset. // offsets: List of offsets relative to base offset. // // Returns: // ERROR_SUCCESS // ERROR_INSUFFICIENT_MEMORY // static int _yr_arena_make_ptr_relocatable( YR_ARENA* arena, uint32_t buffer_id, yr_arena_off_t base_offset, va_list offsets) { size_t offset; int result = ERROR_SUCCESS; // The argument to va_arg is size_t because the offsets passed to this // function are obtained with offsetof(). offset = va_arg(offsets, size_t); while (offset != EOL) { YR_RELOC* reloc = (YR_RELOC*) yr_malloc(sizeof(YR_RELOC)); if (reloc == NULL) return ERROR_INSUFFICIENT_MEMORY; reloc->buffer_id = buffer_id; reloc->offset = base_offset + (yr_arena_off_t) offset; reloc->next = NULL; if (arena->reloc_list_head == NULL) arena->reloc_list_head = reloc; if (arena->reloc_list_tail != NULL) arena->reloc_list_tail->next = reloc; arena->reloc_list_tail = reloc; offset = va_arg(offsets, size_t); } return result; } // Flags for _yr_arena_allocate_memory. #define YR_ARENA_ZERO_MEMORY 1 //////////////////////////////////////////////////////////////////////////////// // Allocates memory in a buffer within the arena. // // Args: // arena: Pointer to the arena. // flags: Flags. The only supported flag so far is YR_ARENA_ZERO_MEMORY. // buffer_id: Buffer number. // size : Size of the region to be allocated. // [out] ref: Pointer to a YR_ARENA_REF that will be updated with the // reference to the newly allocated region. The pointer can be // NULL. // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_INSUFFICIENT_MEMORY // static int _yr_arena_allocate_memory( YR_ARENA* arena, int flags, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref) { if (buffer_id > arena->num_buffers) return ERROR_INVALID_ARGUMENT; YR_ARENA_BUFFER* b = &arena->buffers[buffer_id]; // If the new data doesn't fit in the remaining space the buffer must be // re-sized. This implies moving the buffer to a different memory location // and adjusting the pointers listed in the relocation list. if (b->size - b->used < size) { size_t new_size = (b->size == 0) ? arena->initial_buffer_size : b->size * 2; while (new_size < b->used + size) new_size *= 2; // Make sure that buffer size if not larger than 4GB. if (new_size > 1ULL << 32) return ERROR_INSUFFICIENT_MEMORY; uint8_t* new_data = yr_realloc(b->data, new_size); if (new_data == NULL) return ERROR_INSUFFICIENT_MEMORY; // When yr_realloc uses the Windows API (HeapAlloc, HeapReAlloc) it is not // necessary to set the memory to zero because the API is called with the // HEAP_ZERO_MEMORY flag. #if !defined(_WIN32) && !defined(__CYGWIN__) if (flags & YR_ARENA_ZERO_MEMORY) memset(new_data + b->used, 0, new_size - b->used); #endif YR_RELOC* reloc = arena->reloc_list_head; while (reloc != NULL) { // If the reloc entry is for the same buffer that is being relocated, // the base pointer that we use to access the buffer must be new_data // as arena->buffers[reloc->buffer_id].data (which is the same than // b->data) can't be accessed anymore after the call to yr_realloc. uint8_t* base = buffer_id == reloc->buffer_id ? new_data : arena->buffers[reloc->buffer_id].data; // reloc_address holds the address inside the buffer where the pointer // to be relocated resides. void** reloc_address = (void**) (base + reloc->offset); // reloc_target is the value of the relocatable pointer. void* reloc_target = *reloc_address; if ((uint8_t*) reloc_target >= b->data && (uint8_t*) reloc_target < b->data + b->used) { // reloc_target points to some data inside the buffer being moved, so // the pointer needs to be adjusted. *reloc_address = (uint8_t*) reloc_target - b->data + new_data; } reloc = reloc->next; } b->size = new_size; b->data = new_data; } if (ref != NULL) { ref->buffer_id = buffer_id; ref->offset = (uint32_t) b->used; } b->used += size; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Creates an arena with the specified number of buffers. // // Args: // num_buffers: Number of buffers. // initial_buffer_size: Initial size of each buffer. // [out] arena: Address of a YR_ARENA* pointer that will receive the address // of the newly created arena. // Returns: // ERROR_SUCCESS // ERROR_INSUFFICIENT_MEMORY // int yr_arena_create( uint32_t num_buffers, size_t initial_buffer_size, YR_ARENA** arena) { YR_ARENA* new_arena = (YR_ARENA*) yr_calloc(1, sizeof(YR_ARENA)); if (new_arena == NULL) return ERROR_INSUFFICIENT_MEMORY; new_arena->xrefs = 1; new_arena->num_buffers = num_buffers; new_arena->initial_buffer_size = initial_buffer_size; *arena = new_arena; return ERROR_SUCCESS; } void yr_arena_acquire(YR_ARENA* arena) { arena->xrefs++; } //////////////////////////////////////////////////////////////////////////////// // Releases the arena. // // Decrements the cross-references counter for the arena, if the number of // cross-references reaches zero the arena is destroyed and all its resources // are freed. // // Args: // arena :Pointer to the arena. // // Returns: // ERROR_SUCCESS // ERROR_INSUFFICIENT_MEMORY // int yr_arena_release(YR_ARENA* arena) { arena->xrefs--; if (arena->xrefs > 0) return ERROR_SUCCESS; for (uint32_t i = 0; i < arena->num_buffers; i++) { if (arena->buffers[i].data != NULL) yr_free(arena->buffers[i].data); } YR_RELOC* reloc = arena->reloc_list_head; while (reloc != NULL) { YR_RELOC* next = reloc->next; yr_free(reloc); reloc = next; } yr_free(arena); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Allocates memory in a buffer within the arena. // // Args: // arena: Pointer to the arena. // buffer_id: Buffer number. // size : Size of the region to be allocated. // [out] offset: Pointer to a variable where the function puts the offset // within the buffer of the allocated region. The pointer can // be NULL. // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_INSUFFICIENT_MEMORY // int yr_arena_allocate_memory( YR_ARENA* arena, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref) { return _yr_arena_allocate_memory(arena, 0, buffer_id, size, ref); } //////////////////////////////////////////////////////////////////////////////// // Allocates memory in a buffer within the arena and fill it with zeroes. // // Args: // arena: Pointer to the arena. // buffer_id: Buffer number. // size : Size of the region to be allocated. // [out] offset: Pointer to a variable where the function puts the offset // within the buffer of the allocated region. The pointer can // be NULL. // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_INSUFFICIENT_MEMORY // int yr_arena_allocate_zeroed_memory( YR_ARENA* arena, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref) { return _yr_arena_allocate_memory( arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, ref); } //////////////////////////////////////////////////////////////////////////////// // Allocates a structure within the arena. This function is similar to // yr_arena_allocate_memory but additionally receives a variable-length // list of offsets within the structure where pointers reside. This allows // the arena to keep track of pointers that must be adjusted when memory // is relocated. This is an example on how to invoke this function: // // yr_arena_allocate_struct( // arena, // 0, // sizeof(MY_STRUCTURE), // &ref, // offsetof(MY_STRUCTURE, field_1), // offsetof(MY_STRUCTURE, field_2), // .. // offsetof(MY_STRUCTURE, field_N), // EOL); // // Args: // arena: Pointer to the arena. // buffer_id: Buffer number. // size: Size of the region to be allocated. // [out] ref: Pointer to a reference that will point to the newly allocated // structure when the function returns. The pointer can be NULL // if you don't need the reference. // ... Variable number of offsets relative to the beginning of the struct. // These offsets are of type size_t. // // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_INSUFFICIENT_MEMORY // int yr_arena_allocate_struct( YR_ARENA* arena, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref, ...) { YR_ARENA_REF r; int result = _yr_arena_allocate_memory( arena, YR_ARENA_ZERO_MEMORY, buffer_id, size, &r); if (result != ERROR_SUCCESS) return result; va_list field_offsets; va_start(field_offsets, ref); result = _yr_arena_make_ptr_relocatable( arena, buffer_id, r.offset, field_offsets); va_end(field_offsets); if (result == ERROR_SUCCESS && ref != NULL) { ref->buffer_id = r.buffer_id; ref->offset = r.offset; } return result; } void* yr_arena_get_ptr( YR_ARENA* arena, uint32_t buffer_id, yr_arena_off_t offset) { assert(buffer_id < arena->num_buffers); assert(offset <= arena->buffers[buffer_id].used); return arena->buffers[buffer_id].data + offset; } yr_arena_off_t yr_arena_get_current_offset(YR_ARENA* arena, uint32_t buffer_id) { assert(buffer_id < arena->num_buffers); return (yr_arena_off_t) arena->buffers[buffer_id].used; } int yr_arena_ptr_to_ref(YR_ARENA* arena, const void* address, YR_ARENA_REF* ref) { *ref = YR_ARENA_NULL_REF; if (address == NULL) return 1; for (uint32_t i = 0; i < arena->num_buffers; ++i) { if ((uint8_t*) address >= arena->buffers[i].data && (uint8_t*) address < arena->buffers[i].data + arena->buffers[i].used) { ref->buffer_id = i; ref->offset = (yr_arena_off_t)( (uint8_t*) address - arena->buffers[i].data); return 1; } } return 0; } void* yr_arena_ref_to_ptr(YR_ARENA* arena, YR_ARENA_REF* ref) { if (YR_ARENA_IS_NULL_REF(*ref)) return NULL; #if defined(__arm__) YR_ARENA_REF tmp_ref; memcpy(&tmp_ref, ref, sizeof(YR_ARENA_REF)); ref = &tmp_ref; #endif return yr_arena_get_ptr(arena, ref->buffer_id, ref->offset); } //////////////////////////////////////////////////////////////////////////////// // Tells the arena that certain addresses contains a relocatable pointer. // // Args: // arena: Pointer to the arena. // buffer_id: Buffer number. // ... : Variable number of size_t arguments with offsets within the buffer. // // Returns: // ERROR_SUCCESS if succeed or the corresponding error code otherwise. // int yr_arena_make_ptr_relocatable(YR_ARENA* arena, uint32_t buffer_id, ...) { int result; va_list offsets; va_start(offsets, buffer_id); result = _yr_arena_make_ptr_relocatable(arena, buffer_id, 0, offsets); va_end(offsets); return result; } int yr_arena_write_data( YR_ARENA* arena, uint32_t buffer_id, const void* data, size_t size, YR_ARENA_REF* ref) { YR_ARENA_REF r; // Allocate space in the buffer. FAIL_ON_ERROR(yr_arena_allocate_memory(arena, buffer_id, size, &r)); // Copy the data into the allocated space. memcpy(arena->buffers[buffer_id].data + r.offset, data, size); if (ref != NULL) { ref->buffer_id = r.buffer_id; ref->offset = r.offset; } return ERROR_SUCCESS; } int yr_arena_write_string( YR_ARENA* arena, uint32_t buffer_id, const char* string, YR_ARENA_REF* ref) { return yr_arena_write_data(arena, buffer_id, string, strlen(string) + 1, ref); } int yr_arena_write_uint32( YR_ARENA* arena, uint32_t buffer_id, uint32_t integer, YR_ARENA_REF* ref) { return yr_arena_write_data(arena, buffer_id, &integer, sizeof(integer), ref); } int yr_arena_load_stream(YR_STREAM* stream, YR_ARENA** arena) { YR_ARENA_FILE_HEADER hdr; if (yr_stream_read(&hdr, sizeof(hdr), 1, stream) != 1) return ERROR_INVALID_FILE; if (hdr.magic[0] != 'Y' || hdr.magic[1] != 'A' || hdr.magic[2] != 'R' || hdr.magic[3] != 'A') { return ERROR_INVALID_FILE; } if (hdr.version != YR_ARENA_FILE_VERSION) return ERROR_UNSUPPORTED_FILE_VERSION; if (hdr.num_buffers > YR_MAX_ARENA_BUFFERS) return ERROR_INVALID_FILE; YR_ARENA_FILE_BUFFER buffers[YR_MAX_ARENA_BUFFERS]; size_t read = yr_stream_read( buffers, sizeof(buffers[0]), hdr.num_buffers, stream); if (read != hdr.num_buffers) return ERROR_CORRUPT_FILE; YR_ARENA* new_arena; FAIL_ON_ERROR(yr_arena_create(hdr.num_buffers, 1048576, &new_arena)) for (int i = 0; i < hdr.num_buffers; ++i) { if (buffers[i].size == 0) continue; YR_ARENA_REF ref; FAIL_ON_ERROR_WITH_CLEANUP( yr_arena_allocate_memory(new_arena, i, buffers[i].size, &ref), yr_arena_release(new_arena)) void* ptr = yr_arena_get_ptr(new_arena, i, ref.offset); if (yr_stream_read(ptr, buffers[i].size, 1, stream) != 1) { yr_arena_release(new_arena); return ERROR_CORRUPT_FILE; } } YR_ARENA_REF ref; while (yr_stream_read(&ref, sizeof(ref), 1, stream) == 1) { YR_ARENA_BUFFER* b = &new_arena->buffers[ref.buffer_id]; if (ref.buffer_id >= new_arena->num_buffers || ref.offset > b->used - sizeof(void*)) { yr_arena_release(new_arena); return ERROR_CORRUPT_FILE; } void** reloc_ptr = (void**) (b->data + ref.offset); // Let's convert the reference into a pointer. *reloc_ptr = yr_arena_ref_to_ptr(new_arena, (YR_ARENA_REF*) reloc_ptr); FAIL_ON_ERROR_WITH_CLEANUP( yr_arena_make_ptr_relocatable( new_arena, ref.buffer_id, ref.offset, EOL), yr_arena_release(new_arena)) } *arena = new_arena; return ERROR_SUCCESS; } int yr_arena_save_stream(YR_ARENA* arena, YR_STREAM* stream) { YR_ARENA_FILE_HEADER hdr; hdr.magic[0] = 'Y'; hdr.magic[1] = 'A'; hdr.magic[2] = 'R'; hdr.magic[3] = 'A'; hdr.version = YR_ARENA_FILE_VERSION; hdr.num_buffers = arena->num_buffers; if (yr_stream_write(&hdr, sizeof(hdr), 1, stream) != 1) return ERROR_WRITING_FILE; // The first buffer in the file is after the header and the buffer table, // calculate its offset accordingly. uint64_t offset = sizeof(YR_ARENA_FILE_HEADER) + sizeof(YR_ARENA_FILE_BUFFER) * arena->num_buffers; for (uint32_t i = 0; i < arena->num_buffers; ++i) { YR_ARENA_FILE_BUFFER buffer = { .offset = offset, .size = (uint32_t) arena->buffers[i].used, }; if (yr_stream_write(&buffer, sizeof(buffer), 1, stream) != 1) return ERROR_WRITING_FILE; offset += buffer.size; } // Iterate the relocation list and replace all the relocatable pointers by // references to the buffer and offset where they are pointing to. All // relocatable pointers are expected to be null or point to data stored in // some of the arena's buffers. If a relocatable pointer points outside the // arena that's an error. YR_RELOC* reloc = arena->reloc_list_head; while (reloc != NULL) { // reloc_ptr is a pointer to the relocatable pointer, while *reloc_ptr // is the relocatable pointer itself. void** reloc_ptr = (void**) (arena->buffers[reloc->buffer_id].data + reloc->offset); YR_ARENA_REF ref; #if !defined(NDEBUG) int found = yr_arena_ptr_to_ref(arena, *reloc_ptr, &ref); // yr_arena_ptr_to_ref returns 0 if the relocatable pointer is pointing // outside the arena, this should not happen. assert(found); #else yr_arena_ptr_to_ref(arena, *reloc_ptr, &ref); #endif // Replace the relocatable pointer with a reference that holds information // about the buffer and offset where the relocatable pointer is pointing to. memcpy(reloc_ptr, &ref, sizeof(ref)); reloc = reloc->next; } // Now that all relocatable pointers are converted to references, write the // buffers. for (uint32_t i = 0; i < arena->num_buffers; ++i) { YR_ARENA_BUFFER* b = &arena->buffers[i]; if (b->used > 0) if (yr_stream_write(b->data, b->used, 1, stream) != 1) return ERROR_WRITING_FILE; } // Write the relocation list and restore the pointers back. reloc = arena->reloc_list_head; while (reloc != NULL) { YR_ARENA_REF ref = { .buffer_id = reloc->buffer_id, .offset = reloc->offset, }; if (yr_stream_write(&ref, sizeof(ref), 1, stream) != 1) return ERROR_WRITING_FILE; void** reloc_ptr = (void**) (arena->buffers[reloc->buffer_id].data + reloc->offset); // reloc_ptr is now pointing to a YR_ARENA_REF. YR_ARENA_REF* ref_ptr = (YR_ARENA_REF*) reloc_ptr; // Let's convert the reference into a pointer again. *reloc_ptr = yr_arena_ref_to_ptr(arena, ref_ptr); reloc = reloc->next; } return ERROR_SUCCESS; } yara-4.1.3/libyara/atoms.c000066400000000000000000001212601413423160300153640ustar00rootroot00000000000000/* Copyright (c) 2013-2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This module handles atom extraction from regexps and hex strings. Atoms are undivided substrings found in a regexps and hex strings. Let's consider this hex string: { 01 02 03 04 05 ?? 06 07 08 [1-2] 09 0A } In the above string, byte sequences 0102030405, 060708 and 090A are atoms. Similarly, in this regexp: /abc.*ed[0-9]+fgh/ The strings "abc", "ed" and "fgh" are atoms. When searching for regexps/hex strings matching a file, YARA uses these atoms to find locations inside the file where the regexp/hex string could match. If the atom "abc" is found somewhere inside the file, there is a chance for /abc.*ed[0-9]+fgh/ to match the file, if "abc" doesn't appear in the file there's no chance for the regexp to match. When the atom is found in the file YARA proceeds to fully evaluate the regexp/hex string to determine if it's actually a match. For each regexp/hex string YARA extracts one or more atoms. Sometimes a single atom is enough (like in the previous example "abc" is enough for finding /abc.*ed[0-9]+fgh/), but sometimes a single atom isn't enough like in the regexp /(abc|efg)/. In this case YARA must search for both "abc" AND "efg" and fully evaluate the regexp whenever one of these atoms is found. In the regexp /Look(at|into)this/ YARA can search for "Look", or search for "this", or search for both "at" and "into". This is what we call an atoms tree, because it can be represented by the following tree structure: -OR |- "Look" | |- AND | | | |- "at" | - "into" | - "this" From an atom tree YARA chooses the best combination, trying to minimize the number of required atoms, but also using high quality atoms (long atoms with not too many zeroes and a bit of byte diversity). In the previous example YARA will end up using the "Look" atom alone, but in /a(bcd|efg)h/ atoms "bcd" and "efg" will be used because "a" and "h" are too short. */ #include #include #include #include #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////// // Returns a numeric value indicating the quality of an atom. The quality // depends on some characteristics of the atom, including its length, number // of very common bytes like 00 and FF and number of unique distinct bytes. // Atom 00 00 has a very low quality, because it's only two bytes long and // both bytes are zeroes. Atom 01 01 01 01 is better but still not optimal, // because the same byte is repeated. Atom 01 02 03 04 is an optimal one. // // Args: // config: Pointer to YR_ATOMS_CONFIG struct. // atom: Pointer to YR_ATOM struct. // // Returns: // An integer indicating the atom's quality // int yr_atoms_heuristic_quality(YR_ATOMS_CONFIG* config, YR_ATOM* atom) { YR_BITMASK seen_bytes[YR_BITMASK_SIZE(256)]; int quality = 0; int unique_bytes = 0; int i; assert(atom->length <= YR_MAX_ATOM_LENGTH); yr_bitmask_clear_all(seen_bytes); for (i = 0; i < atom->length; i++) { switch (atom->mask[i]) { case 0x00: quality -= 6; break; case 0x0F: quality += 1; break; case 0xF0: quality += 1; break; case 0xFF: switch (atom->bytes[i]) { case 0x00: case 0x20: case 0xCC: case 0xFF: // Common bytes contribute less to the quality than the rest. quality += 15; break; default: // Bytes in the a-z and A-Z ranges have a slightly lower quality // than the rest. We want to favor atoms that contain bytes outside // those ranges because they generate less additional atoms during // calls to _yr_atoms_case_combinations. if (yr_lowercase[atom->bytes[i]] >= 'a' && yr_lowercase[atom->bytes[i]] <= 'z') quality += 19; else quality += 20; }; if (!yr_bitmask_is_set(seen_bytes, atom->bytes[i])) { yr_bitmask_set(seen_bytes, atom->bytes[i]); unique_bytes++; } } } // If all the bytes in the atom are equal and very common, let's penalize // it heavily. if (unique_bytes == 1 && (yr_bitmask_is_set(seen_bytes, 0x00) || yr_bitmask_is_set(seen_bytes, 0x20) || yr_bitmask_is_set(seen_bytes, 0xCC) || yr_bitmask_is_set(seen_bytes, 0xFF))) { quality -= 10 * atom->length; } return YR_MAX_ATOM_QUALITY - 20 * YR_MAX_ATOM_LENGTH + quality; } //////////////////////////////////////////////////////////////////////////////// // Compares the byte sequence in a1 with the YR_ATOM in a2, taking atom's mask // into account. // // Returns: // < 0 if the first byte that does not match has a lower value in a1 than // in a2. // > 0 if the first byte that does not match has a greater value in a1 than // in a2. // = 0 if a1 is equal or matches a2. // static int _yr_atoms_cmp(const uint8_t* a1, YR_ATOM* a2) { int result = 0; int i = 0; while (result == 0 && i < a2->length) { switch (a2->mask[i]) { case 0xFF: case 0x0F: case 0xF0: case 0x00: result = (a1[i] & a2->mask[i]) - a2->bytes[i]; break; default: assert(false); } i++; } return result; } //////////////////////////////////////////////////////////////////////////////// // Returns a numeric value indicating the quality of an atom. The quality is // based in the atom quality table passed in "config". Very common atoms // (i.e: those with greater quality) have lower quality than those that are // uncommon. See the comment for yr_compiler_set_atom_quality_table for // details about the quality table's format. // // Args: // YR_ATOMS_CONFIG* config - Pointer to YR_ATOMS_CONFIG struct. // YR_ATOM* atom - Pointer to YR_ATOM struct. // // Returns: // An integer indicating the atom's quality // int yr_atoms_table_quality(YR_ATOMS_CONFIG* config, YR_ATOM* atom) { YR_ATOM_QUALITY_TABLE_ENTRY* table = config->quality_table; int begin = 0; int end = config->quality_table_entries; assert(atom->length <= YR_MAX_ATOM_LENGTH); while (end > begin) { int middle = begin + (end - begin) / 2; int c = _yr_atoms_cmp(table[middle].atom, atom); if (c < 0) { begin = middle + 1; } else if (c > 0) { end = middle; } else { int i = middle + 1; int quality = table[middle].quality; int min_quality = quality; while (i < end && _yr_atoms_cmp(table[i].atom, atom) == 0) { if (min_quality > table[i].quality) min_quality = table[i].quality; i++; } i = middle - 1; while (i >= begin && _yr_atoms_cmp(table[i].atom, atom) == 0) { if (min_quality > table[i].quality) min_quality = table[i].quality; i--; } return min_quality >> (YR_MAX_ATOM_LENGTH - atom->length); } } return YR_MAX_ATOM_QUALITY; } //////////////////////////////////////////////////////////////////////////////// // Returns the quality for the worst quality atom in a list. // int yr_atoms_min_quality(YR_ATOMS_CONFIG* config, YR_ATOM_LIST_ITEM* atom_list) { YR_ATOM_LIST_ITEM* atom; int quality; int min_quality = YR_MAX_ATOM_QUALITY; if (atom_list == NULL) return YR_MIN_ATOM_QUALITY; atom = atom_list; while (atom != NULL) { quality = config->get_atom_quality(config, &atom->atom); if (quality < min_quality) min_quality = quality; atom = atom->next; } return min_quality; } //////////////////////////////////////////////////////////////////////////////// // Creates a new node for an atoms tree. // static YR_ATOM_TREE_NODE* _yr_atoms_tree_node_create(uint8_t type) { YR_ATOM_TREE_NODE* new_node = (YR_ATOM_TREE_NODE*) yr_malloc( sizeof(YR_ATOM_TREE_NODE)); if (new_node != NULL) { new_node->type = type; new_node->atom.length = 0; new_node->next_sibling = NULL; new_node->children_head = NULL; new_node->children_tail = NULL; } return new_node; } //////////////////////////////////////////////////////////////////////////////// // Destroys a node from an atoms tree. // static void _yr_atoms_tree_node_destroy(YR_ATOM_TREE_NODE* node) { YR_ATOM_TREE_NODE* child; YR_ATOM_TREE_NODE* next_child; if (node == NULL) return; if (node->type == ATOM_TREE_OR || node->type == ATOM_TREE_AND) { child = node->children_head; while (child != NULL) { next_child = child->next_sibling; _yr_atoms_tree_node_destroy(child); child = next_child; } } yr_free(node); } //////////////////////////////////////////////////////////////////////////////// // Appends a new child node to another atoms tree node. // static void _yr_atoms_tree_node_append( YR_ATOM_TREE_NODE* dest, YR_ATOM_TREE_NODE* node) { if (dest->children_head == NULL) dest->children_head = node; if (dest->children_tail != NULL) dest->children_tail->next_sibling = node; dest->children_tail = node; } //////////////////////////////////////////////////////////////////////////////// // Destroys an atoms tree. // static void _yr_atoms_tree_destroy(YR_ATOM_TREE* atom_tree) { _yr_atoms_tree_node_destroy(atom_tree->root_node); yr_free(atom_tree); } //////////////////////////////////////////////////////////////////////////////// // Destroys an atoms list. // void yr_atoms_list_destroy(YR_ATOM_LIST_ITEM* list_head) { YR_ATOM_LIST_ITEM* item = list_head; YR_ATOM_LIST_ITEM* next; while (item != NULL) { next = item->next; yr_free(item); item = next; } } //////////////////////////////////////////////////////////////////////////////// // Concats two atoms lists. // static YR_ATOM_LIST_ITEM* _yr_atoms_list_concat( YR_ATOM_LIST_ITEM* list1, YR_ATOM_LIST_ITEM* list2) { YR_ATOM_LIST_ITEM* item; if (list1 == NULL) return list2; item = list1; while (item->next != NULL) { item = item->next; } item->next = list2; return list1; } //////////////////////////////////////////////////////////////////////////////// // If the atom starts or ends with an unknown byte (mask == 0x00), trim // those bytes out of the atom. We don't want to expand an atom like // { ?? 01 02 } into { 00 01 02 }, { 01 01 02}, { 02 01 02} .. { FF 01 02} // in those cases it's better to simply have a shorter atom { 01 02 }. // // Args: // atom: Pointer to the YR_ATOM to be trimmed. // // Returns: // The number of bytes that were trimmed from the beginning of the atom. // int _yr_atoms_trim(YR_ATOM* atom) { int mask_00 = 0; int mask_ff = 0; int trim_left = 0; while (trim_left < atom->length && atom->mask[trim_left] == 0) trim_left++; while (atom->length > trim_left && atom->mask[atom->length - 1] == 0) atom->length--; atom->length -= trim_left; if (atom->length == 0) return 0; // The trimmed atom goes from trim_left to trim_left + atom->length and the // first and last byte in the atom are known (mask == 0xFF). Now count the // number of known and unknown bytes in the atom (mask == 0xFF and // mask == 0x00 respectively). for (int i = 0; i < atom->length; i++) { if (atom->mask[trim_left + i] == 0xFF) mask_ff++; else if (atom->mask[trim_left + i] == 0x00) mask_00++; } // If the number of unknown bytes is >= than the number of known bytes // it doesn't make sense the to use this atom, so we use a single byte atom // containing the first known byte. If YR_MAX_ATOM_LENGTH == 4 this happens // only when the atom is like { XX ?? ?? YY }, so using the first known // byte is good enough. For larger values of YR_MAX_ATOM_LENGTH this is not // the most efficient solution, as better atoms could be choosen. For // example, in { XX ?? ?? ?? YY ZZ } the best atom is { YY ZZ } not { XX }. // But let's keep it like this for simplicity. if (mask_00 >= mask_ff) atom->length = 1; if (trim_left == 0) return 0; // Shift bytes and mask trim_left positions to the left. for (int i = 0; i < YR_MAX_ATOM_LENGTH - trim_left; i++) { atom->bytes[i] = atom->bytes[trim_left + i]; atom->mask[i] = atom->mask[trim_left + i]; } return trim_left; } //////////////////////////////////////////////////////////////////////////////// // This function receives an atom tree and returns a list of atoms to be added // to the Aho-Corasick automaton. // static int _yr_atoms_choose( YR_ATOMS_CONFIG* config, YR_ATOM_TREE_NODE* node, YR_ATOM_LIST_ITEM** chosen_atoms, int* atoms_quality) { YR_ATOM_TREE_NODE* child; YR_ATOM_LIST_ITEM* item; YR_ATOM_LIST_ITEM* tail; int shift, quality; int max_quality = YR_MIN_ATOM_QUALITY; int min_quality = YR_MAX_ATOM_QUALITY; *chosen_atoms = NULL; *atoms_quality = YR_MIN_ATOM_QUALITY; switch (node->type) { case ATOM_TREE_LEAF: item = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM)); if (item == NULL) return ERROR_INSUFFICIENT_MEMORY; memcpy(&item->atom, &node->atom, sizeof(YR_ATOM)); shift = _yr_atoms_trim(&item->atom); if (item->atom.length > 0) { item->forward_code_ref = node->re_nodes[shift]->forward_code_ref; item->backward_code_ref = node->re_nodes[shift]->backward_code_ref; item->backtrack = 0; item->next = NULL; *chosen_atoms = item; *atoms_quality = config->get_atom_quality(config, &item->atom); } else { yr_free(item); } break; case ATOM_TREE_OR: // The choosen nodes are those coming from the highest quality child. child = node->children_head; while (child != NULL) { FAIL_ON_ERROR(_yr_atoms_choose(config, child, &item, &quality)); if (quality > max_quality) { max_quality = quality; yr_atoms_list_destroy(*chosen_atoms); *chosen_atoms = item; } else { yr_atoms_list_destroy(item); } if (max_quality == YR_MAX_ATOM_QUALITY) break; child = child->next_sibling; } *atoms_quality = max_quality; break; case ATOM_TREE_AND: // The choosen nodes are the concatenation of the the nodes choosen from // all the children. child = node->children_head; while (child != NULL) { FAIL_ON_ERROR(_yr_atoms_choose(config, child, &item, &quality)); if (quality < min_quality) min_quality = quality; if (item != NULL) { tail = item; while (tail->next != NULL) tail = tail->next; tail->next = *chosen_atoms; *chosen_atoms = item; } child = child->next_sibling; } *atoms_quality = min_quality; break; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Returns all combinations of lower and upper cases for a given atom. For // atom "abc" the output would be "abc" "abC" "aBC" and so on. Resulting // atoms are written into the output buffer in this format: // // [size of atom 1] [atom 1] ... [size of atom N] [atom N] [0] // // Notice the zero at the end to indicate where the output ends. // // The caller is responsible of providing a buffer large enough to hold the // returned atoms. // static uint8_t* _yr_atoms_case_combinations( uint8_t* atom, int atom_length, int atom_offset, uint8_t* output_buffer) { uint8_t c; uint8_t* new_atom; if (atom_offset + 1 < atom_length) output_buffer = _yr_atoms_case_combinations( atom, atom_length, atom_offset + 1, output_buffer); c = atom[atom_offset]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { // Write atom length. *output_buffer = atom_length; output_buffer++; memcpy(output_buffer, atom, atom_length); new_atom = output_buffer; output_buffer += atom_length; // Swap character case. if (c >= 'a' && c <= 'z') new_atom[atom_offset] -= 32; else new_atom[atom_offset] += 32; if (atom_offset + 1 < atom_length) output_buffer = _yr_atoms_case_combinations( new_atom, atom_length, atom_offset + 1, output_buffer); } if (atom_offset == 0) *output_buffer = 0; return output_buffer; } // Size of buffer used in _yr_atoms_case_insensitive for storing the all // the possible combinations for an atom. Each atom has up to YR_MAX_ATOM_LENGTH // characters and each character has two possible values (upper and lower case). // That means 2 ^ YR_MAX_ATOM_LENGTH combinations for an atom, where each atom // occupies YR_MAX_ATOM_LENGTH + 1 bytes (the atom itself +1 byte for its // length). One extra bytes is allocated for the zero value indicating the end. #define CASE_COMBINATIONS_BUFFER_SIZE \ (1 << YR_MAX_ATOM_LENGTH) * (YR_MAX_ATOM_LENGTH + 1) + 1 //////////////////////////////////////////////////////////////////////////////// // For a given list of atoms returns another list of atoms // with every case combination. // static int _yr_atoms_case_insensitive( YR_ATOM_LIST_ITEM* atoms, YR_ATOM_LIST_ITEM** case_insensitive_atoms) { YR_ATOM_LIST_ITEM* atom; YR_ATOM_LIST_ITEM* new_atom; uint8_t buffer[CASE_COMBINATIONS_BUFFER_SIZE]; uint8_t atom_length; uint8_t* atoms_cursor; int i; *case_insensitive_atoms = NULL; atom = atoms; while (atom != NULL) { _yr_atoms_case_combinations(atom->atom.bytes, atom->atom.length, 0, buffer); atoms_cursor = buffer; atom_length = *atoms_cursor; atoms_cursor++; while (atom_length != 0) { new_atom = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM)); if (new_atom == NULL) return ERROR_INSUFFICIENT_MEMORY; for (i = 0; i < atom_length; i++) { new_atom->atom.bytes[i] = atoms_cursor[i]; new_atom->atom.mask[i] = 0xFF; } new_atom->atom.length = atom_length; new_atom->forward_code_ref = atom->forward_code_ref; new_atom->backward_code_ref = atom->backward_code_ref; new_atom->backtrack = atom->backtrack; new_atom->next = *case_insensitive_atoms; *case_insensitive_atoms = new_atom; atoms_cursor += atom_length; atom_length = *atoms_cursor; atoms_cursor++; } atom = atom->next; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // For a given list of atoms returns another list after a single byte xor // has been applied to it. // static int _yr_atoms_xor( YR_ATOM_LIST_ITEM* atoms, uint8_t min, uint8_t max, YR_ATOM_LIST_ITEM** xor_atoms) { YR_ATOM_LIST_ITEM* atom; YR_ATOM_LIST_ITEM* new_atom; int i, j; *xor_atoms = NULL; atom = atoms; while (atom != NULL) { for (j = min; j <= max; j++) { new_atom = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM)); if (new_atom == NULL) return ERROR_INSUFFICIENT_MEMORY; for (i = 0; i < atom->atom.length; i++) { new_atom->atom.bytes[i] = atom->atom.bytes[i] ^ j; new_atom->atom.mask[i] = 0xFF; } new_atom->atom.length = yr_min(atom->atom.length, YR_MAX_ATOM_LENGTH); new_atom->forward_code_ref = atom->forward_code_ref; new_atom->backward_code_ref = atom->backward_code_ref; new_atom->backtrack = atom->backtrack; new_atom->next = *xor_atoms; *xor_atoms = new_atom; } atom = atom->next; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // For a given list of atoms returns another list with the corresponding // wide atoms. Wide atoms are just the original atoms with interleaved zeroes, // for example: 01 02 -> 01 00 02 00 // static int _yr_atoms_wide( YR_ATOM_LIST_ITEM* atoms, YR_ATOM_LIST_ITEM** wide_atoms) { YR_ATOM_LIST_ITEM* atom; YR_ATOM_LIST_ITEM* new_atom; int i; *wide_atoms = NULL; atom = atoms; while (atom != NULL) { new_atom = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM)); if (new_atom == NULL) return ERROR_INSUFFICIENT_MEMORY; for (i = 0; i < YR_MAX_ATOM_LENGTH; i++) { new_atom->atom.bytes[i] = 0; new_atom->atom.mask[i] = 0xFF; } for (i = 0; i < atom->atom.length; i++) { if (i * 2 < YR_MAX_ATOM_LENGTH) new_atom->atom.bytes[i * 2] = atom->atom.bytes[i]; else break; } new_atom->atom.length = yr_min(atom->atom.length * 2, YR_MAX_ATOM_LENGTH); new_atom->forward_code_ref = atom->forward_code_ref; new_atom->backward_code_ref = atom->backward_code_ref; new_atom->backtrack = atom->backtrack * 2; new_atom->next = *wide_atoms; *wide_atoms = new_atom; atom = atom->next; } return ERROR_SUCCESS; } struct STACK_ITEM { RE_NODE* re_node; YR_ATOM_TREE_NODE* new_appending_node; }; #define make_atom_from_re_nodes(atom, nodes_length, nodes) \ { \ atom.length = nodes_length; \ for (i = 0; i < atom.length; i++) \ { \ atom.bytes[i] = (uint8_t)(recent_re_nodes)[i]->value; \ atom.mask[i] = (uint8_t)(recent_re_nodes)[i]->mask; \ } \ } //////////////////////////////////////////////////////////////////////////////// // Extract atoms from a regular expression. This is a helper function used by // yr_atoms_extract_from_re that receives the abstract syntax tree for a regexp // (or hex pattern) and builds an atom tree. The appending_node argument is a // pointer to the ATOM_TREE_OR node at the root of the atom tree. This function // creates the tree by appending new nodes to it. // static int _yr_atoms_extract_from_re( YR_ATOMS_CONFIG* config, RE_AST* re_ast, YR_ATOM_TREE_NODE* appending_node) { YR_STACK* stack; RE_NODE* re_node; YR_ATOM atom; YR_ATOM best_atom; struct STACK_ITEM si; int i, shift; int quality; int best_quality = -1; int n = 0; YR_ATOM_TREE_NODE* and_node; YR_ATOM_TREE_NODE* left_node; YR_ATOM_TREE_NODE* right_node; // The RE_NODEs most recently visited that can conform an atom (ie: // RE_NODE_LITERAL, RE_NODE_MASKED_LITERAL and RE_NODE_ANY). The number of // items in this array is n. RE_NODE* recent_re_nodes[YR_MAX_ATOM_LENGTH]; // The RE_NODEs corresponding to the best atom found so far for the current // appending node. RE_NODE* best_atom_re_nodes[YR_MAX_ATOM_LENGTH]; // This holds the ATOM_TREE_OR node where leaves (ATOM_TREE_LEAF) are // currently being appended. YR_ATOM_TREE_NODE* current_appending_node = NULL; // This holds the ATOM_TREE_LEAF node whose atom is currently being updated. YR_ATOM_TREE_NODE* leaf = NULL; FAIL_ON_ERROR(yr_stack_create(1024, sizeof(si), &stack)); // This first item pushed in the stack is the last one to be poped out, the // sole purpose of this item is forcing that any pending leaf is appended to // current_appending_node during the last iteration of the loop. si.re_node = NULL; si.new_appending_node = appending_node; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, (void*) &si), yr_stack_destroy(stack)); // Start processing the root node. si.re_node = re_ast->root_node; // Leaf nodes are initially appended to the node passed in the appending_node, // argument which is the root ATOM_TREE_OR node that is empty at this point. si.new_appending_node = appending_node; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, (void*) &si), yr_stack_destroy(stack)); while (yr_stack_pop(stack, (void*) &si)) { // Change the appending node if the item poped from the stack says so. if (si.new_appending_node != NULL) { // Before changing the appending node let's append any pending leaf to // the current appending node. if (n > 0) { make_atom_from_re_nodes(atom, n, recent_re_nodes); shift = _yr_atoms_trim(&atom); quality = config->get_atom_quality(config, &atom); FAIL_ON_NULL_WITH_CLEANUP( leaf = _yr_atoms_tree_node_create(ATOM_TREE_LEAF), yr_stack_destroy(stack)); if (quality > best_quality) { memcpy(&leaf->atom, &atom, sizeof(atom)); memcpy( &leaf->re_nodes, &recent_re_nodes[shift], sizeof(recent_re_nodes) - shift * sizeof(recent_re_nodes[0])); } else { memcpy(&leaf->atom, &best_atom, sizeof(best_atom)); memcpy( &leaf->re_nodes, &best_atom_re_nodes, sizeof(best_atom_re_nodes)); } _yr_atoms_tree_node_append(current_appending_node, leaf); n = 0; } current_appending_node = si.new_appending_node; } if (si.re_node != NULL) { switch (si.re_node->type) { case RE_NODE_LITERAL: case RE_NODE_MASKED_LITERAL: case RE_NODE_ANY: if (n < YR_MAX_ATOM_LENGTH) { recent_re_nodes[n] = si.re_node; best_atom_re_nodes[n] = si.re_node; best_atom.bytes[n] = (uint8_t) si.re_node->value; best_atom.mask[n] = (uint8_t) si.re_node->mask; best_atom.length = ++n; } else if (best_quality < YR_MAX_ATOM_QUALITY) { make_atom_from_re_nodes(atom, n, recent_re_nodes); shift = _yr_atoms_trim(&atom); quality = config->get_atom_quality(config, &atom); if (quality > best_quality) { for (i = 0; i < atom.length; i++) { best_atom.bytes[i] = atom.bytes[i]; best_atom.mask[i] = atom.mask[i]; best_atom_re_nodes[i] = recent_re_nodes[i + shift]; } best_atom.length = atom.length; best_quality = quality; } for (i = 1; i < YR_MAX_ATOM_LENGTH; i++) recent_re_nodes[i - 1] = recent_re_nodes[i]; recent_re_nodes[YR_MAX_ATOM_LENGTH - 1] = si.re_node; } break; case RE_NODE_CONCAT: re_node = si.re_node->children_tail; // Push children right to left, they are poped left to right. while (re_node != NULL) { si.new_appending_node = NULL; si.re_node = re_node; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); re_node = re_node->prev_sibling; } break; case RE_NODE_ALT: // Create ATOM_TREE_AND node with two ATOM_TREE_OR children nodes. and_node = _yr_atoms_tree_node_create(ATOM_TREE_AND); left_node = _yr_atoms_tree_node_create(ATOM_TREE_OR); right_node = _yr_atoms_tree_node_create(ATOM_TREE_OR); if (and_node == NULL || left_node == NULL || right_node == NULL) { _yr_atoms_tree_node_destroy(and_node); _yr_atoms_tree_node_destroy(left_node); _yr_atoms_tree_node_destroy(right_node); yr_stack_destroy(stack); return ERROR_INSUFFICIENT_MEMORY; } and_node->children_head = left_node; and_node->children_tail = right_node; left_node->next_sibling = right_node; // Add the ATOM_TREE_AND as children of the current node. _yr_atoms_tree_node_append(current_appending_node, and_node); re_node = si.re_node; si.new_appending_node = current_appending_node; si.re_node = NULL; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); // RE_NODE_ALT nodes has only two children, so children_head is the // left one, and children_tail is right one. si.new_appending_node = right_node; si.re_node = re_node->children_tail; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); si.new_appending_node = left_node; si.re_node = re_node->children_head; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); break; case RE_NODE_PLUS: re_node = si.re_node; si.new_appending_node = current_appending_node; si.re_node = NULL; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); si.new_appending_node = NULL; // RE_NODE_PLUS nodes has a single child, which is children_head. si.re_node = re_node->children_head; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); break; case RE_NODE_RANGE: re_node = si.re_node; si.new_appending_node = current_appending_node; si.re_node = NULL; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); si.new_appending_node = NULL; // RE_NODE_RANGE nodes has a single child, which is children_head. si.re_node = re_node->children_head; // In a regexp like /a{10,20}/ the optimal atom is 'aaaa' (assuming // that YR_MAX_ATOM_LENGTH = 4) because the 'a' character must appear // at least 10 times in the matching string. Each call in the loop // will append one 'a' to the atom, so YR_MAX_ATOM_LENGTH iterations // are enough. for (i = 0; i < yr_min(re_node->start, YR_MAX_ATOM_LENGTH); i++) { FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); } break; case RE_NODE_RANGE_ANY: case RE_NODE_STAR: case RE_NODE_CLASS: case RE_NODE_WORD_CHAR: case RE_NODE_NON_WORD_CHAR: case RE_NODE_SPACE: case RE_NODE_NON_SPACE: case RE_NODE_DIGIT: case RE_NODE_NON_DIGIT: case RE_NODE_EMPTY: case RE_NODE_ANCHOR_START: case RE_NODE_ANCHOR_END: case RE_NODE_WORD_BOUNDARY: case RE_NODE_NON_WORD_BOUNDARY: si.new_appending_node = current_appending_node; si.re_node = NULL; FAIL_ON_ERROR_WITH_CLEANUP( yr_stack_push(stack, &si), yr_stack_destroy(stack)); break; default: assert(false); } } } yr_stack_destroy(stack); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Makes an exact copy of an YR_ATOM_LIST_ITEM. // static YR_ATOM_LIST_ITEM* _yr_atoms_clone_list_item(YR_ATOM_LIST_ITEM* item) { YR_ATOM_LIST_ITEM* clone = (YR_ATOM_LIST_ITEM*) yr_malloc( sizeof(YR_ATOM_LIST_ITEM)); if (clone == NULL) return NULL; memcpy(clone, item, sizeof(YR_ATOM_LIST_ITEM)); return clone; } //////////////////////////////////////////////////////////////////////////////// // Given list of atoms that may contain wildcards, replace those wildcarded // atoms with a list of non-wildcarded atoms covering all the combinations // allowed by the wildcarded atom. For example, the atom {01 ?2 03} will be // replaced by {01 02 03}, {01 12 03}, {01 22 03} .. {01 F2 03}. The list // is modified in-place. // // Args: // atoms: Pointer to first element of the list. // // Returns: // ERROR_SUCCESS or ERROR_INSUFFICIENT_MEMORY. // static int _yr_atoms_expand_wildcards(YR_ATOM_LIST_ITEM* atoms) { int i; YR_ATOM_LIST_ITEM* atom = atoms; YR_ATOM_LIST_ITEM* new_atom; YR_ATOM_LIST_ITEM* prev_atom; YR_ATOM_LIST_ITEM* next_atom; while (atom != NULL) { bool expanded = false; for (i = 0; i < atom->atom.length; i++) { uint16_t a, s, e, incr = 1; switch (atom->atom.mask[i]) { case 0x00: expanded = true; s = 0x00; e = 0xFF; break; case 0x0F: expanded = true; s = atom->atom.bytes[i]; e = atom->atom.bytes[i] | 0xF0; incr = 0x10; break; case 0xF0: expanded = true; s = atom->atom.bytes[i]; e = atom->atom.bytes[i] | 0x0F; break; default: s = 0; e = 0; } if (s != e) { atom->atom.bytes[i] = (uint8_t) s; atom->atom.mask[i] = 0xFF; } prev_atom = atom; next_atom = atom->next; for (a = s + incr; a <= e; a += incr) { new_atom = _yr_atoms_clone_list_item(atom); if (new_atom == NULL) return ERROR_INSUFFICIENT_MEMORY; new_atom->atom.bytes[i] = (uint8_t) a; new_atom->atom.mask[i] = 0xFF; new_atom->next = next_atom; prev_atom->next = new_atom; prev_atom = new_atom; } } if (!expanded) atom = atom->next; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Extract atoms from a regular expression. This function receives the abstract // syntax tree for a regexp (or hex pattern) and returns a list of atoms that // should be added to the Aho-Corasick automaton. // int yr_atoms_extract_from_re( YR_ATOMS_CONFIG* config, RE_AST* re_ast, YR_MODIFIER modifier, YR_ATOM_LIST_ITEM** atoms, int* min_atom_quality) { YR_ATOM_TREE* atom_tree = (YR_ATOM_TREE*) yr_malloc(sizeof(YR_ATOM_TREE)); YR_ATOM_LIST_ITEM* wide_atoms; YR_ATOM_LIST_ITEM* case_insensitive_atoms; if (atom_tree == NULL) return ERROR_INSUFFICIENT_MEMORY; atom_tree->root_node = _yr_atoms_tree_node_create(ATOM_TREE_OR); if (atom_tree->root_node == NULL) { _yr_atoms_tree_destroy(atom_tree); return ERROR_INSUFFICIENT_MEMORY; } FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_extract_from_re(config, re_ast, atom_tree->root_node), _yr_atoms_tree_destroy(atom_tree)); // Initialize atom list *atoms = NULL; // Choose the atoms that will be used. FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_choose(config, atom_tree->root_node, atoms, min_atom_quality), _yr_atoms_tree_destroy(atom_tree)); _yr_atoms_tree_destroy(atom_tree); FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_expand_wildcards(*atoms), { // Cleanup yr_atoms_list_destroy(*atoms); *atoms = NULL; }); // Don't do convert atoms to wide here if either base64 modifier is used. // This is to avoid the situation where we have "base64 wide" because // the wide has already been applied BEFORE the base64 encoding. if (modifier.flags & STRING_FLAGS_WIDE && !(modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE)) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_wide(*atoms, &wide_atoms), { // Cleanup yr_atoms_list_destroy(*atoms); yr_atoms_list_destroy(wide_atoms); *atoms = NULL; }); if (modifier.flags & STRING_FLAGS_ASCII) { *atoms = _yr_atoms_list_concat(*atoms, wide_atoms); } else { yr_atoms_list_destroy(*atoms); *atoms = wide_atoms; } } if (modifier.flags & STRING_FLAGS_NO_CASE) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_case_insensitive(*atoms, &case_insensitive_atoms), { // Cleanup yr_atoms_list_destroy(*atoms); yr_atoms_list_destroy(case_insensitive_atoms); *atoms = NULL; }); *atoms = _yr_atoms_list_concat(*atoms, case_insensitive_atoms); } // No atoms has been extracted, let's add a zero-length atom. if (*atoms == NULL) { *atoms = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM)); if (*atoms == NULL) return ERROR_INSUFFICIENT_MEMORY; (*atoms)->atom.length = 0; (*atoms)->backtrack = 0; (*atoms)->forward_code_ref = re_ast->root_node->forward_code_ref; (*atoms)->backward_code_ref = YR_ARENA_NULL_REF; (*atoms)->next = NULL; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Extract atoms from a string. // int yr_atoms_extract_from_string( YR_ATOMS_CONFIG* config, uint8_t* string, int32_t string_length, YR_MODIFIER modifier, YR_ATOM_LIST_ITEM** atoms, int* min_atom_quality) { YR_ATOM_LIST_ITEM* item; YR_ATOM_LIST_ITEM* case_insensitive_atoms; YR_ATOM_LIST_ITEM* xor_atoms; YR_ATOM_LIST_ITEM* wide_atoms; YR_ATOM atom; int quality, max_quality; int i; item = (YR_ATOM_LIST_ITEM*) yr_malloc(sizeof(YR_ATOM_LIST_ITEM)); if (item == NULL) return ERROR_INSUFFICIENT_MEMORY; item->forward_code_ref = YR_ARENA_NULL_REF; item->backward_code_ref = YR_ARENA_NULL_REF; item->next = NULL; item->backtrack = 0; item->atom.length = yr_min(string_length, YR_MAX_ATOM_LENGTH); for (i = 0; i < item->atom.length; i++) { item->atom.bytes[i] = string[i]; item->atom.mask[i] = 0xFF; } max_quality = config->get_atom_quality(config, &item->atom); atom.length = YR_MAX_ATOM_LENGTH; memset(atom.mask, 0xFF, atom.length); for (i = YR_MAX_ATOM_LENGTH; i < string_length && max_quality < YR_MAX_ATOM_QUALITY; i++) { atom.length = YR_MAX_ATOM_LENGTH; memcpy(atom.bytes, string + i - YR_MAX_ATOM_LENGTH + 1, atom.length); quality = config->get_atom_quality(config, &atom); if (quality > max_quality) { memcpy(&item->atom, &atom, sizeof(atom)); item->backtrack = i - YR_MAX_ATOM_LENGTH + 1; max_quality = quality; } } *atoms = item; *min_atom_quality = max_quality; if (modifier.flags & STRING_FLAGS_WIDE) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_wide(*atoms, &wide_atoms), { // Cleanup yr_atoms_list_destroy(*atoms); yr_atoms_list_destroy(wide_atoms); *atoms = NULL; }); if (modifier.flags & STRING_FLAGS_ASCII) { *atoms = _yr_atoms_list_concat(*atoms, wide_atoms); } else { yr_atoms_list_destroy(*atoms); *atoms = wide_atoms; } } if (modifier.flags & STRING_FLAGS_NO_CASE) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_case_insensitive(*atoms, &case_insensitive_atoms), { // Cleanup yr_atoms_list_destroy(*atoms); yr_atoms_list_destroy(case_insensitive_atoms); *atoms = NULL; }); *atoms = _yr_atoms_list_concat(*atoms, case_insensitive_atoms); } if (modifier.flags & STRING_FLAGS_XOR) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_atoms_xor(*atoms, modifier.xor_min, modifier.xor_max, &xor_atoms), { // Cleanup yr_atoms_list_destroy(*atoms); yr_atoms_list_destroy(xor_atoms); *atoms = NULL; }); yr_atoms_list_destroy(*atoms); *atoms = xor_atoms; } // Recheck the atom quality, in case we have just generated some poor atoms. // https://github.com/VirusTotal/yara/issues/1172 for (item = *atoms; item != NULL; item = item->next) { quality = config->get_atom_quality(config, &item->atom); if (quality < *min_atom_quality) *min_atom_quality = quality; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Prints an atom tree node. Used only for debugging purposes. // void yr_atoms_tree_node_print(YR_ATOM_TREE_NODE* node) { YR_ATOM_TREE_NODE* child; if (node == NULL) { printf("Empty tree node\n"); return; } switch (node->type) { case ATOM_TREE_LEAF: for (int i = 0; i < node->atom.length; i++) printf("%02X", node->atom.bytes[i]); break; case ATOM_TREE_AND: case ATOM_TREE_OR: if (node->type == ATOM_TREE_AND) printf("AND"); else printf("OR"); printf("("); child = node->children_head; while (child != NULL) { yr_atoms_tree_node_print(child); child = child->next_sibling; if (child != NULL) printf(","); } printf(")"); break; } } yara-4.1.3/libyara/base64.c000066400000000000000000000317431413423160300153330ustar00rootroot00000000000000/* Copyright (c) 2020. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////// // Given a pointer to a SIZED_STRING append 0, 1 or 2 bytes and base64 encode // the string. The number of padding bytes is returned in "pad" and the caller // is expected to trim the appropriate number of leading and trailing bytes. // // This is based upon the ideas at: // https://www.leeholmes.com/blog/2019/12/10/searching-for-content-in-base-64-strings-2/ // // The caller is responsible for freeing the returned string. // static SIZED_STRING* _yr_modified_base64_encode( SIZED_STRING* in, SIZED_STRING* alphabet, int i, int* pad) { uint8_t* src = (uint8_t*) in->c_string; size_t len = in->length; SIZED_STRING* out; uint8_t* p; uint8_t* end; char* alphabet_str = alphabet->c_string; uint8_t* tmp; int j; *pad = ((i + len) % 3) ? 3 - ((i + len) % 3) : 0; // Add "i" for the number of prepended bytes. out = (SIZED_STRING*) yr_malloc( sizeof(SIZED_STRING) + i + ((len * 4 + 3) / 3) + *pad); if (out == NULL) return NULL; tmp = (uint8_t*) yr_malloc(sizeof(uint8_t) * (len + i)); if (tmp == NULL) { yr_free(out); return NULL; } // Prepend appropriate number of bytes and copy remaining input bytes into // temporary buffer. for (j = 0; j < i; j++) tmp[j] = 'A'; memcpy(tmp + j, src, len); src = tmp; p = (uint8_t*) out->c_string; end = src + len + j; while (end - src >= 3) { *p++ = alphabet_str[src[0] >> 2]; *p++ = alphabet_str[((src[0] & 0x03) << 4 | src[1] >> 4)]; *p++ = alphabet_str[((src[1] & 0x0f) << 2 | (src[2] >> 6))]; *p++ = alphabet_str[src[2] & 0x3f]; src += 3; } // Handle remaining bytes and padding. if (end - src) { *p++ = alphabet_str[src[0] >> 2]; if (end - src == 1) { *p++ = alphabet_str[(src[0] & 0x03) << 4]; *p++ = '='; } else { *p++ = alphabet_str[((src[0] & 0x03) << 4 | src[1] >> 4)]; *p++ = alphabet_str[(src[1] & 0x0f) << 2]; } *p++ = '='; } yr_free(tmp); out->length = (uint32_t)(p - (uint8_t*) out->c_string); return out; } //////////////////////////////////////////////////////////////////////////////// // Given a base64 encoded string, return a new string with leading and trailing // bytes stripped appropriately. The number of leading bytes to skip is always // (i + 1) or zero when no leading bytes are added and the number of trailing // bytes is always (pad + 1) or zero when pad is zero. Also, convert the final // string to wide if desired. // // Note: This implementation assumes you only prepend 0, 1 or 2 bytes. // static SIZED_STRING* _yr_base64_get_base64_substring( SIZED_STRING* encoded_str, int wide, int i, int pad) { SIZED_STRING* new_str; SIZED_STRING* final_str; char* start; uint32_t length; int trailing; int leading; trailing = pad ? pad + 1 : 0; leading = i ? i + 1 : 0; length = encoded_str->length - (leading + trailing); new_str = (SIZED_STRING*) yr_malloc(sizeof(SIZED_STRING) + length); if (new_str == NULL) return NULL; start = encoded_str->c_string + leading; memcpy(new_str->c_string, start, length); new_str->length = length; new_str->c_string[length] = '\0'; if (wide) { final_str = ss_convert_to_wide(new_str); yr_free(new_str); } else { final_str = new_str; } return final_str; } // RE metacharacters which need to be escaped when generating the final RE. #define IS_METACHAR(x) \ (x == '\\' || x == '^' || x == '$' || x == '|' || x == '(' || x == ')' || \ x == '[' || x == ']' || x == '*' || x == '?' || x == '{' || x == ',' || \ x == '.' || x == '+' || x == '}') //////////////////////////////////////////////////////////////////////////////// // Given a SIZED_STRING return the number of characters which will need to be // escaped when generating the final string to pass to the regexp compiler. // static int _yr_base64_count_escaped(SIZED_STRING* str) { int c = 0; for (uint32_t i = 0; i < str->length; i++) { // We must be careful to escape null bytes because they break the RE lexer. if (IS_METACHAR(str->c_string[i])) c++; else if (str->c_string[i] == '\x00') c += 4; } return c; } //////////////////////////////////////////////////////////////////////////////// // Create nodes representing the different encodings of a base64 string. // static int _yr_base64_create_nodes( SIZED_STRING* str, SIZED_STRING* alphabet, int wide, BASE64_NODE** head, BASE64_NODE** tail) { SIZED_STRING* encoded_str; SIZED_STRING* final_str; BASE64_NODE* node; int pad; for (int i = 0; i <= 2; i++) { if (i == 1 && str->length == 1) continue; node = (BASE64_NODE*) yr_malloc(sizeof(BASE64_NODE)); if (node == NULL) return ERROR_INSUFFICIENT_MEMORY; FAIL_ON_NULL_WITH_CLEANUP( encoded_str = _yr_modified_base64_encode(str, alphabet, i, &pad), yr_free(node)); // Now take the encoded string and strip the bytes which are affected by // the leading and trailing bytes of the plaintext. FAIL_ON_NULL_WITH_CLEANUP( final_str = _yr_base64_get_base64_substring(encoded_str, wide, i, pad), { yr_free(encoded_str); yr_free(node); }); yr_free(encoded_str); node->str = final_str; node->escaped = _yr_base64_count_escaped(node->str); node->next = NULL; if (*head == NULL) *head = node; if (*tail == NULL) { *tail = node; } else { (*tail)->next = node; *tail = node; } } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Useful for printing the encoded strings. // void _yr_base64_print_nodes(BASE64_NODE* head) { BASE64_NODE* p = head; while (p != NULL) { for (size_t i = 0; i < p->str->length; i++) { if (p->str->c_string[i] >= 32 && p->str->c_string[i] <= 126) printf("%c", p->str->c_string[i]); else printf("\\x%02x", p->str->c_string[i]); } printf("\n"); p = p->next; } } //////////////////////////////////////////////////////////////////////////////// // Destroy a list of base64 nodes. // static void _yr_base64_destroy_nodes(BASE64_NODE* head) { BASE64_NODE* p = head; BASE64_NODE* next; while (p != NULL) { yr_free(p->str); next = p->next; yr_free(p); p = next; } } //////////////////////////////////////////////////////////////////////////////// // Create the regexp that is the alternatives of each of the strings collected // in the BASE64_NODE list. // int _yr_base64_create_regexp( BASE64_NODE* head, RE_AST** re_ast, RE_ERROR* re_error) { BASE64_NODE* p = head; char* re_str; char* s; uint32_t length = 0; // The number of nodes in the list, used to know how many '|'. uint32_t c = 0; while (p != NULL) { length += (p->str->length + p->escaped); c++; p = p->next; } if (c == 0) return ERROR_INSUFFICIENT_MEMORY; // Make sure to include '(' and ')'. // The number of '|' is number of nodes - 1. re_str = (char*) yr_malloc(length + 2 + (c - 1) + 1); if (re_str == NULL) return ERROR_INSUFFICIENT_MEMORY; s = re_str; p = head; *s++ = '('; while (p != NULL) { for (uint32_t i = 0; i < p->str->length; i++) { if (IS_METACHAR(p->str->c_string[i])) *s++ = '\\'; if (p->str->c_string[i] == '\x00') { *s++ = '\\'; *s++ = 'x'; *s++ = '0'; *s++ = '0'; } else *s++ = p->str->c_string[i]; } if (p->next != NULL) *s++ = '|'; p = p->next; } *s++ = ')'; *s = '\x00'; // Useful for debugging as long as the string has no NULL bytes in it. ;) // printf("%s\n", re_str); FAIL_ON_ERROR_WITH_CLEANUP( yr_re_parse(re_str, re_ast, re_error), yr_free(re_str)); yr_free(re_str); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Given a string and an alphabet, generate the RE_AST suitable for representing // the different encodings of the string. This means we generate // "(ABCD|EFGH|IJKL)" and must be careful to escape any special characters as // a result of the base64 encoding. // // This uses ideas from: // https://www.leeholmes.com/blog/2019/12/10/searching-for-content-in-base-64-strings-2/ // // This does not emit the code for the RE. A further call to yr_re_ast_emit_code // is required to get the code. // int yr_base64_ast_from_string( SIZED_STRING* in_str, YR_MODIFIER modifier, RE_AST** re_ast, RE_ERROR* error) { BASE64_NODE* head = NULL; BASE64_NODE* tail = NULL; SIZED_STRING* wide_str; if (modifier.flags & STRING_FLAGS_WIDE) { wide_str = ss_convert_to_wide(in_str); if (modifier.flags & STRING_FLAGS_BASE64) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_nodes(wide_str, modifier.alphabet, 0, &head, &tail), { // Cleanup strcpy(error->message, "Failure encoding base64 wide string"); yr_free(wide_str); _yr_base64_destroy_nodes(head); }); } if (modifier.flags & STRING_FLAGS_BASE64_WIDE) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_nodes(wide_str, modifier.alphabet, 1, &head, &tail), { // Cleanup strcpy(error->message, "Failure encoding base64wide wide string"); yr_free(wide_str); _yr_base64_destroy_nodes(head); }); } yr_free(wide_str); } if (modifier.flags & STRING_FLAGS_ASCII) { if (modifier.flags & STRING_FLAGS_BASE64) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_nodes(in_str, modifier.alphabet, 0, &head, &tail), { // Cleanup strcpy(error->message, "Failure encoding base64 ascii string"); _yr_base64_destroy_nodes(head); }); } if (modifier.flags & STRING_FLAGS_BASE64_WIDE) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_nodes(in_str, modifier.alphabet, 1, &head, &tail), { // Cleanup strcpy(error->message, "Failure encoding base64wide ascii string"); _yr_base64_destroy_nodes(head); }); } } if (!(modifier.flags & STRING_FLAGS_WIDE) && !(modifier.flags & STRING_FLAGS_ASCII)) { if (modifier.flags & STRING_FLAGS_BASE64) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_nodes(in_str, modifier.alphabet, 0, &head, &tail), { // Cleanup strcpy(error->message, "Failure encoding base64 string"); _yr_base64_destroy_nodes(head); }); } if (modifier.flags & STRING_FLAGS_BASE64_WIDE) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_nodes(in_str, modifier.alphabet, 1, &head, &tail), { // Cleanup strcpy(error->message, "Failure encoding base64wide string"); _yr_base64_destroy_nodes(head); }); } } // Useful for printing the contents of the nodes, to make sure they were // encoded and stripped properly. //_yr_base64_print_nodes(head); // Create the final regex string to be parsed from all the nodes. // Error message is filled in by the caller in case of failure. FAIL_ON_ERROR_WITH_CLEANUP( _yr_base64_create_regexp(head, re_ast, error), _yr_base64_destroy_nodes(head)); _yr_base64_destroy_nodes(head); return ERROR_SUCCESS; } yara-4.1.3/libyara/bitmask.c000066400000000000000000000100061413423160300156660ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include //////////////////////////////////////////////////////////////////////////////// // Find the smallest offset within bitmask A where bitmask B can be accommodated // without bit collisions. A collision occurs when both bitmasks have a bit set // to 1 at the same offset. This function assumes that the first bit in B is 1 // and do optimizations that rely on that. // // The function also receives a pointer to an uint32_t where the function stores // a value that is used for speeding-up subsequent searches over the same // bitmask A. When called for the first time with some bitmask A, the pointer // must point to a zero-initialized uint32_t. In the next call the function uses // the previously stored value for skipping over a portion of the A bitmask and // updates the value. // // Args: // a: Bitmask A // b: Bitmask B // len_a: Length of bitmask A in bits // len_b: Length of bitmask B in bits // off_a: Address of an uint32_t indicating the offset within bitmask A where // to start searching. In the first call to it must point to a 0 value. // This function updates the value to use it in subsequent calls. // Returns: // The smaller offset within bitmask A where bitmask B can be put. // uint32_t yr_bitmask_find_non_colliding_offset( YR_BITMASK* a, YR_BITMASK* b, uint32_t len_a, uint32_t len_b, uint32_t* off_a) { uint32_t i, j, k; // Ensure that the first bit of bitmask B is set, as this function does some // optimizations that rely on that. assert(yr_bitmask_is_set(b, 0)); // Skip all slots that are filled with 1s. It's safe to do that because the // first bit of B is 1, so we won't be able to accommodate B at any offset // within such slots. for (i = *off_a / YR_BITMASK_SLOT_BITS; i <= len_a / YR_BITMASK_SLOT_BITS && a[i] == -1L; i++) ; *off_a = i; for (; i <= len_a / YR_BITMASK_SLOT_BITS; i++) { // The slot is filled with 1s, we can safely skip it. if (a[i] == -1L) continue; for (j = 0; j <= yr_min(len_a, YR_BITMASK_SLOT_BITS - 1); j++) { bool found = true; for (k = 0; k <= len_b / YR_BITMASK_SLOT_BITS; k++) { YR_BITMASK m = b[k] << j; if (j > 0 && k > 0) m |= b[k - 1] >> (YR_BITMASK_SLOT_BITS - j); if ((i + k <= len_a / YR_BITMASK_SLOT_BITS) && (m & a[i + k]) != 0) { found = false; break; } } if (found) return i * YR_BITMASK_SLOT_BITS + j; } } return len_a; } yara-4.1.3/libyara/compiler.c000066400000000000000000000670301413423160300160570ustar00rootroot00000000000000/* Copyright (c) 2013-2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #ifdef _MSC_VER #include #include #else #include #endif #include #include #include #include #include #include #include #include #include static void _yr_compiler_default_include_free( const char* callback_result_ptr, void* user_data) { if (callback_result_ptr != NULL) { yr_free((void*) callback_result_ptr); } } const char* _yr_compiler_default_include_callback( const char* include_name, const char* calling_rule_filename, const char* calling_rule_namespace, void* user_data) { #ifndef _MSC_VER struct stat stbuf; #endif char* file_buffer; #ifdef _MSC_VER long file_size; #else off_t file_size; #endif int fd = -1; #if defined(_MSC_VER) _sopen_s(&fd, include_name, _O_RDONLY | _O_BINARY, _SH_DENYWR, _S_IREAD); #elif defined(_WIN32) || defined(__CYGWIN__) fd = open(include_name, O_RDONLY | O_BINARY); #else fd = open(include_name, O_RDONLY); #endif if (fd == -1) return NULL; #ifdef _MSC_VER file_size = _filelength(fd); if (file_size == -1) { _close(fd); return NULL; } #else if ((fstat(fd, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) { close(fd); return NULL; } file_size = stbuf.st_size; #endif file_buffer = (char*) yr_malloc((size_t) file_size + 1); if (file_buffer == NULL) { #ifdef _MSC_VER _close(fd); #else close(fd); #endif return NULL; } if (file_size != read(fd, file_buffer, (size_t) file_size)) { yr_free(file_buffer); #ifdef _MSC_VER _close(fd); #else close(fd); #endif return NULL; } else { file_buffer[file_size] = '\0'; } #ifdef _MSC_VER _close(fd); #else close(fd); #endif return file_buffer; } //////////////////////////////////////////////////////////////////////////////// // Returns a rule given its index in the rules table. // // The returned pointer is valid as long as no other rule is written to the // table. This is because the write operation may cause the table to be moved to // a different location in memory. Use the pointer only in a limited scope where // you can be sure that no other rule is being written during the pointer's // lifetime. // YR_RULE* _yr_compiler_get_rule_by_idx(YR_COMPILER* compiler, uint32_t rule_idx) { return (YR_RULE*) yr_arena_get_ptr( compiler->arena, YR_RULES_TABLE, rule_idx * sizeof(YR_RULE)); } //////////////////////////////////////////////////////////////////////////////// // Stores some data in the YR_SZ_POOL and returns a reference to it. // // If the same data was already stored in a previous call to this function the // data is not written again, a reference to the existing data is returned // instead. // int _yr_compiler_store_data( YR_COMPILER* compiler, const void* data, size_t data_length, YR_ARENA_REF* ref) { // Check if the data is already in YR_SZ_POOL by using a hash table. uint32_t offset = yr_hash_table_lookup_uint32_raw_key( compiler->sz_table, data, data_length, NULL); if (offset == UINT32_MAX) { // The data was not previously written to YR_SZ_POOL, write it and store // the reference's offset in the hash table. Storing the buffer number // is not necessary, it's always YR_SZ_POOL. FAIL_ON_ERROR(yr_arena_write_data( compiler->arena, YR_SZ_POOL, data, data_length, ref)); FAIL_ON_ERROR(yr_hash_table_add_uint32_raw_key( compiler->sz_table, data, data_length, NULL, ref->offset)); } else { ref->buffer_id = YR_SZ_POOL; ref->offset = offset; } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Similar to _yr_compiler_store_data, but receives a null-terminated string. // int _yr_compiler_store_string( YR_COMPILER* compiler, const char* string, YR_ARENA_REF* ref) { return _yr_compiler_store_data( compiler, (void*) string, strlen(string) + 1, // include the null terminator ref); } YR_API int yr_compiler_create(YR_COMPILER** compiler) { int result; YR_COMPILER* new_compiler; new_compiler = (YR_COMPILER*) yr_calloc(1, sizeof(YR_COMPILER)); if (new_compiler == NULL) return ERROR_INSUFFICIENT_MEMORY; new_compiler->current_rule_idx = UINT32_MAX; new_compiler->next_rule_idx = 0; new_compiler->current_string_idx = 0; new_compiler->current_namespace_idx = 0; new_compiler->current_meta_idx = 0; new_compiler->num_namespaces = 0; new_compiler->errors = 0; new_compiler->callback = NULL; new_compiler->rules = NULL; new_compiler->include_callback = _yr_compiler_default_include_callback; new_compiler->incl_clbk_user_data = NULL; new_compiler->include_free = _yr_compiler_default_include_free; new_compiler->re_ast_callback = NULL; new_compiler->re_ast_clbk_user_data = NULL; new_compiler->last_error = ERROR_SUCCESS; new_compiler->last_error_line = 0; new_compiler->current_line = 0; new_compiler->file_name_stack_ptr = 0; new_compiler->fixup_stack_head = NULL; new_compiler->loop_index = -1; new_compiler->loop_for_of_var_index = -1; new_compiler->atoms_config.get_atom_quality = yr_atoms_heuristic_quality; new_compiler->atoms_config.quality_warning_threshold = YR_ATOM_QUALITY_WARNING_THRESHOLD; result = yr_hash_table_create(5000, &new_compiler->rules_table); if (result == ERROR_SUCCESS) result = yr_hash_table_create(1000, &new_compiler->objects_table); if (result == ERROR_SUCCESS) result = yr_hash_table_create(10000, &new_compiler->strings_table); if (result == ERROR_SUCCESS) result = yr_hash_table_create(10000, &new_compiler->sz_table); if (result == ERROR_SUCCESS) result = yr_arena_create(YR_NUM_SECTIONS, 1048576, &new_compiler->arena); if (result == ERROR_SUCCESS) result = yr_ac_automaton_create( new_compiler->arena, &new_compiler->automaton); if (result == ERROR_SUCCESS) { *compiler = new_compiler; } else // if error, do cleanup { yr_compiler_destroy(new_compiler); } return result; } YR_API void yr_compiler_destroy(YR_COMPILER* compiler) { yr_arena_release(compiler->arena); if (compiler->automaton != NULL) yr_ac_automaton_destroy(compiler->automaton); yr_hash_table_destroy(compiler->rules_table, NULL); yr_hash_table_destroy(compiler->strings_table, NULL); yr_hash_table_destroy(compiler->sz_table, NULL); yr_hash_table_destroy( compiler->objects_table, (YR_HASH_TABLE_FREE_VALUE_FUNC) yr_object_destroy); if (compiler->atoms_config.free_quality_table) yr_free(compiler->atoms_config.quality_table); for (int i = 0; i < compiler->file_name_stack_ptr; i++) yr_free(compiler->file_name_stack[i]); YR_FIXUP* fixup = compiler->fixup_stack_head; while (fixup != NULL) { YR_FIXUP* next_fixup = fixup->next; yr_free(fixup); fixup = next_fixup; } yr_free(compiler); } YR_API void yr_compiler_set_callback( YR_COMPILER* compiler, YR_COMPILER_CALLBACK_FUNC callback, void* user_data) { compiler->callback = callback; compiler->user_data = user_data; } YR_API void yr_compiler_set_include_callback( YR_COMPILER* compiler, YR_COMPILER_INCLUDE_CALLBACK_FUNC include_callback, YR_COMPILER_INCLUDE_FREE_FUNC include_free, void* user_data) { compiler->include_callback = include_callback; compiler->include_free = include_free; compiler->incl_clbk_user_data = user_data; } YR_API void yr_compiler_set_re_ast_callback( YR_COMPILER* compiler, YR_COMPILER_RE_AST_CALLBACK_FUNC re_ast_callback, void* user_data) { compiler->re_ast_callback = re_ast_callback; compiler->re_ast_clbk_user_data = user_data; } //////////////////////////////////////////////////////////////////////////////// // This function allows to specify an atom quality table to be used by the // compiler for choosing the best atoms from regular expressions and strings. // When a quality table is set, the compiler uses yr_atoms_table_quality // instead of yr_atoms_heuristic_quality for computing atom quality. The table // has an arbitrary number of entries, each composed of YR_MAX_ATOM_LENGTH + 1 // bytes. The first YR_MAX_ATOM_LENGTH bytes from each entry are the atom's // ones, and the remaining byte is a value in the range 0-255 determining the // atom's quality. Entries must be lexicographically sorted by atom in ascending // order. // // [ atom (YR_MAX_ATOM_LENGTH bytes) ] [ quality (1 byte) ] // // [ 00 00 .. 00 00 ] [ 00 ] // [ 00 00 .. 00 01 ] [ 45 ] // [ 00 00 .. 00 02 ] [ 13 ] // ... // [ FF FF .. FF FF ] [ 03 ] // // The "table" argument must point to a buffer containing the quality in // the format explained above, and "entries" must contain the number of entries // in the table. The table can not be freed while the compiler is in use, the // caller is responsible for freeing the table. // // The "warning_threshold" argument must be a number between 0 and 255, if some // atom chosen for a string have a quality below the specified threshold a // warning like " is slowing down scanning" is shown. // YR_API void yr_compiler_set_atom_quality_table( YR_COMPILER* compiler, const void* table, int entries, unsigned char warning_threshold) { compiler->atoms_config.free_quality_table = false; compiler->atoms_config.quality_warning_threshold = warning_threshold; compiler->atoms_config.get_atom_quality = yr_atoms_table_quality; compiler->atoms_config.quality_table_entries = entries; compiler->atoms_config.quality_table = (YR_ATOM_QUALITY_TABLE_ENTRY*) table; } //////////////////////////////////////////////////////////////////////////////// // Load an atom quality table from a file. The file's content must have the // format explained in the description for yr_compiler_set_atom_quality_table. // YR_API int yr_compiler_load_atom_quality_table( YR_COMPILER* compiler, const char* filename, unsigned char warning_threshold) { long file_size; int entries; void* table; FILE* fh = fopen(filename, "rb"); if (fh == NULL) return ERROR_COULD_NOT_OPEN_FILE; fseek(fh, 0L, SEEK_END); file_size = ftell(fh); fseek(fh, 0L, SEEK_SET); if (file_size == -1L) { fclose(fh); return ERROR_COULD_NOT_READ_FILE; } table = yr_malloc(file_size); if (table == NULL) { fclose(fh); return ERROR_INSUFFICIENT_MEMORY; } entries = (int) file_size / sizeof(YR_ATOM_QUALITY_TABLE_ENTRY); if (fread(table, sizeof(YR_ATOM_QUALITY_TABLE_ENTRY), entries, fh) != entries) { fclose(fh); yr_free(table); return ERROR_COULD_NOT_READ_FILE; } fclose(fh); yr_compiler_set_atom_quality_table( compiler, table, entries, warning_threshold); compiler->atoms_config.free_quality_table = true; return ERROR_SUCCESS; } int _yr_compiler_push_file_name(YR_COMPILER* compiler, const char* file_name) { char* str; int i; for (i = 0; i < compiler->file_name_stack_ptr; i++) { if (strcmp(file_name, compiler->file_name_stack[i]) == 0) return ERROR_INCLUDES_CIRCULAR_REFERENCE; } if (compiler->file_name_stack_ptr == YR_MAX_INCLUDE_DEPTH) return ERROR_INCLUDE_DEPTH_EXCEEDED; str = yr_strdup(file_name); if (str == NULL) return ERROR_INSUFFICIENT_MEMORY; compiler->file_name_stack[compiler->file_name_stack_ptr] = str; compiler->file_name_stack_ptr++; return ERROR_SUCCESS; } void _yr_compiler_pop_file_name(YR_COMPILER* compiler) { if (compiler->file_name_stack_ptr > 0) { compiler->file_name_stack_ptr--; yr_free(compiler->file_name_stack[compiler->file_name_stack_ptr]); compiler->file_name_stack[compiler->file_name_stack_ptr] = NULL; } } int _yr_compiler_get_var_frame(YR_COMPILER* compiler) { int i, result = 0; for (i = 0; i < compiler->loop_index; i++) { result += compiler->loop[i].vars_count + compiler->loop[i].vars_internal_count; } return result; } YR_API char* yr_compiler_get_current_file_name(YR_COMPILER* compiler) { if (compiler->file_name_stack_ptr > 0) { return compiler->file_name_stack[compiler->file_name_stack_ptr - 1]; } else { return NULL; } } static int _yr_compiler_set_namespace( YR_COMPILER* compiler, const char* namespace_) { YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, 0); bool found = false; for (int i = 0; i < compiler->num_namespaces; i++, ns++) { if (strcmp(ns->name, namespace_) == 0) { found = true; compiler->current_namespace_idx = i; break; } } if (!found) { YR_ARENA_REF ref; FAIL_ON_ERROR(yr_arena_allocate_struct( compiler->arena, YR_NAMESPACES_TABLE, sizeof(YR_NAMESPACE), &ref, offsetof(YR_NAMESPACE, name), EOL)); ns = (YR_NAMESPACE*) yr_arena_ref_to_ptr(compiler->arena, &ref); FAIL_ON_ERROR(_yr_compiler_store_string(compiler, namespace_, &ref)); ns->name = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref); ns->idx = compiler->num_namespaces; compiler->current_namespace_idx = compiler->num_namespaces; compiler->num_namespaces++; } return ERROR_SUCCESS; } YR_API int yr_compiler_add_file( YR_COMPILER* compiler, FILE* rules_file, const char* namespace_, const char* file_name) { int result; // Don't allow yr_compiler_add_file() after // yr_compiler_get_rules() has been called. assert(compiler->rules == NULL); // Don't allow calls to yr_compiler_add_file() if a previous call to // yr_compiler_add_XXXX failed. assert(compiler->errors == 0); if (namespace_ != NULL) compiler->last_error = _yr_compiler_set_namespace(compiler, namespace_); else compiler->last_error = _yr_compiler_set_namespace(compiler, "default"); if (compiler->last_error == ERROR_SUCCESS && file_name != NULL) compiler->last_error = _yr_compiler_push_file_name(compiler, file_name); if (compiler->last_error != ERROR_SUCCESS) return ++compiler->errors; result = yr_lex_parse_rules_file(rules_file, compiler); if (file_name != NULL) _yr_compiler_pop_file_name(compiler); return result; } YR_API int yr_compiler_add_fd( YR_COMPILER* compiler, YR_FILE_DESCRIPTOR rules_fd, const char* namespace_, const char* file_name) { int result; // Don't allow yr_compiler_add_fd() after // yr_compiler_get_rules() has been called. assert(compiler->rules == NULL); // Don't allow calls to yr_compiler_add_fd() if a previous call to // yr_compiler_add_XXXX failed. assert(compiler->errors == 0); if (namespace_ != NULL) compiler->last_error = _yr_compiler_set_namespace(compiler, namespace_); else compiler->last_error = _yr_compiler_set_namespace(compiler, "default"); if (compiler->last_error == ERROR_SUCCESS && file_name != NULL) compiler->last_error = _yr_compiler_push_file_name(compiler, file_name); if (compiler->last_error != ERROR_SUCCESS) return ++compiler->errors; result = yr_lex_parse_rules_fd(rules_fd, compiler); if (file_name != NULL) _yr_compiler_pop_file_name(compiler); return result; } YR_API int yr_compiler_add_string( YR_COMPILER* compiler, const char* rules_string, const char* namespace_) { // Don't allow calls to yr_compiler_add_string() after // yr_compiler_get_rules() has been called. assert(compiler->rules == NULL); // Don't allow calls to yr_compiler_add_string() if a previous call to // yr_compiler_add_XXXX failed. assert(compiler->errors == 0); if (namespace_ != NULL) compiler->last_error = _yr_compiler_set_namespace(compiler, namespace_); else compiler->last_error = _yr_compiler_set_namespace(compiler, "default"); if (compiler->last_error != ERROR_SUCCESS) return ++compiler->errors; return yr_lex_parse_rules_string(rules_string, compiler); } static int _yr_compiler_compile_rules(YR_COMPILER* compiler) { YR_RULE null_rule; YR_EXTERNAL_VARIABLE null_external; uint8_t halt = OP_HALT; // Write halt instruction at the end of code. FAIL_ON_ERROR(yr_arena_write_data( compiler->arena, YR_CODE_SECTION, &halt, sizeof(uint8_t), NULL)); // Write a null rule indicating the end. memset(&null_rule, 0xFA, sizeof(YR_RULE)); null_rule.flags = RULE_FLAGS_NULL; FAIL_ON_ERROR(yr_arena_write_data( compiler->arena, YR_RULES_TABLE, &null_rule, sizeof(YR_RULE), NULL)); // Write a null external indicating the end. memset(&null_external, 0xFA, sizeof(YR_EXTERNAL_VARIABLE)); null_external.type = EXTERNAL_VARIABLE_TYPE_NULL; FAIL_ON_ERROR(yr_arena_write_data( compiler->arena, YR_EXTERNAL_VARIABLES_TABLE, &null_external, sizeof(YR_EXTERNAL_VARIABLE), NULL)); // Write Aho-Corasick automaton to arena. FAIL_ON_ERROR(yr_ac_compile(compiler->automaton, compiler->arena)); YR_ARENA_REF ref; FAIL_ON_ERROR(yr_arena_allocate_struct( compiler->arena, YR_SUMMARY_SECTION, sizeof(YR_SUMMARY), &ref, EOL)); YR_SUMMARY* summary = (YR_SUMMARY*) yr_arena_ref_to_ptr( compiler->arena, &ref); summary->num_namespaces = compiler->num_namespaces; summary->num_rules = compiler->next_rule_idx; summary->num_strings = compiler->current_string_idx; return yr_rules_from_arena(compiler->arena, &compiler->rules); } YR_API int yr_compiler_get_rules(YR_COMPILER* compiler, YR_RULES** rules) { // Don't allow calls to yr_compiler_get_rules() if a previous call to // yr_compiler_add_XXXX failed. assert(compiler->errors == 0); *rules = NULL; if (compiler->rules == NULL) FAIL_ON_ERROR(_yr_compiler_compile_rules(compiler)); *rules = compiler->rules; return ERROR_SUCCESS; } static int _yr_compiler_define_variable( YR_COMPILER* compiler, YR_EXTERNAL_VARIABLE* external) { YR_EXTERNAL_VARIABLE* ext; YR_OBJECT* object; if (external->identifier == NULL) return ERROR_INVALID_ARGUMENT; object = (YR_OBJECT*) yr_hash_table_lookup( compiler->objects_table, external->identifier, NULL); if (object != NULL) return ERROR_DUPLICATED_EXTERNAL_VARIABLE; YR_ARENA_REF ext_ref; YR_ARENA_REF ref; FAIL_ON_ERROR(yr_arena_allocate_struct( compiler->arena, YR_EXTERNAL_VARIABLES_TABLE, sizeof(YR_EXTERNAL_VARIABLE), &ext_ref, offsetof(YR_EXTERNAL_VARIABLE, identifier), EOL)); ext = (YR_EXTERNAL_VARIABLE*) yr_arena_ref_to_ptr(compiler->arena, &ext_ref); FAIL_ON_ERROR( _yr_compiler_store_string(compiler, external->identifier, &ref)); ext->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref); ext->type = external->type; ext->value = external->value; if (external->type == EXTERNAL_VARIABLE_TYPE_STRING) { if (external->value.s == NULL) return ERROR_INVALID_ARGUMENT; FAIL_ON_ERROR(_yr_compiler_store_string(compiler, external->value.s, &ref)); FAIL_ON_ERROR(yr_arena_make_ptr_relocatable( compiler->arena, YR_EXTERNAL_VARIABLES_TABLE, ext_ref.offset + offsetof(YR_EXTERNAL_VARIABLE, value.s), EOL)); ext->value.s = (char*) yr_arena_ref_to_ptr(compiler->arena, &ref); } FAIL_ON_ERROR(yr_object_from_external_variable(external, &object)); FAIL_ON_ERROR_WITH_CLEANUP( yr_hash_table_add( compiler->objects_table, external->identifier, NULL, (void*) object), yr_object_destroy(object)); return ERROR_SUCCESS; } YR_API int yr_compiler_define_integer_variable( YR_COMPILER* compiler, const char* identifier, int64_t value) { YR_EXTERNAL_VARIABLE external; external.type = EXTERNAL_VARIABLE_TYPE_INTEGER; external.identifier = identifier; external.value.i = value; FAIL_ON_ERROR(_yr_compiler_define_variable(compiler, &external)); return ERROR_SUCCESS; } YR_API int yr_compiler_define_boolean_variable( YR_COMPILER* compiler, const char* identifier, int value) { YR_EXTERNAL_VARIABLE external; external.type = EXTERNAL_VARIABLE_TYPE_BOOLEAN; external.identifier = identifier; external.value.i = value; FAIL_ON_ERROR(_yr_compiler_define_variable(compiler, &external)); return ERROR_SUCCESS; } YR_API int yr_compiler_define_float_variable( YR_COMPILER* compiler, const char* identifier, double value) { YR_EXTERNAL_VARIABLE external; external.type = EXTERNAL_VARIABLE_TYPE_FLOAT; external.identifier = identifier; external.value.f = value; FAIL_ON_ERROR(_yr_compiler_define_variable(compiler, &external)); return ERROR_SUCCESS; } YR_API int yr_compiler_define_string_variable( YR_COMPILER* compiler, const char* identifier, const char* value) { YR_EXTERNAL_VARIABLE external; external.type = EXTERNAL_VARIABLE_TYPE_STRING; external.identifier = identifier; external.value.s = (char*) value; FAIL_ON_ERROR(_yr_compiler_define_variable(compiler, &external)); return ERROR_SUCCESS; } YR_API char* yr_compiler_get_error_message( YR_COMPILER* compiler, char* buffer, int buffer_size) { uint32_t max_strings_per_rule; switch (compiler->last_error) { case ERROR_INSUFFICIENT_MEMORY: snprintf(buffer, buffer_size, "not enough memory"); break; case ERROR_DUPLICATED_IDENTIFIER: snprintf( buffer, buffer_size, "duplicated identifier \"%s\"", compiler->last_error_extra_info); break; case ERROR_DUPLICATED_STRING_IDENTIFIER: snprintf( buffer, buffer_size, "duplicated string identifier \"%s\"", compiler->last_error_extra_info); break; case ERROR_DUPLICATED_TAG_IDENTIFIER: snprintf( buffer, buffer_size, "duplicated tag identifier \"%s\"", compiler->last_error_extra_info); break; case ERROR_DUPLICATED_META_IDENTIFIER: snprintf( buffer, buffer_size, "duplicated metadata identifier \"%s\"", compiler->last_error_extra_info); break; case ERROR_DUPLICATED_LOOP_IDENTIFIER: snprintf( buffer, buffer_size, "duplicated loop identifier \"%s\"", compiler->last_error_extra_info); break; case ERROR_UNDEFINED_STRING: snprintf( buffer, buffer_size, "undefined string \"%s\"", compiler->last_error_extra_info); break; case ERROR_UNDEFINED_IDENTIFIER: snprintf( buffer, buffer_size, "undefined identifier \"%s\"", compiler->last_error_extra_info); break; case ERROR_UNREFERENCED_STRING: snprintf( buffer, buffer_size, "unreferenced string \"%s\"", compiler->last_error_extra_info); break; case ERROR_EMPTY_STRING: snprintf( buffer, buffer_size, "empty string \"%s\"", compiler->last_error_extra_info); break; case ERROR_NOT_A_STRUCTURE: snprintf( buffer, buffer_size, "\"%s\" is not a structure", compiler->last_error_extra_info); break; case ERROR_NOT_INDEXABLE: snprintf( buffer, buffer_size, "\"%s\" is not an array or dictionary", compiler->last_error_extra_info); break; case ERROR_NOT_A_FUNCTION: snprintf( buffer, buffer_size, "\"%s\" is not a function", compiler->last_error_extra_info); break; case ERROR_INVALID_FIELD_NAME: snprintf( buffer, buffer_size, "invalid field name \"%s\"", compiler->last_error_extra_info); break; case ERROR_MISPLACED_ANONYMOUS_STRING: snprintf(buffer, buffer_size, "wrong use of anonymous string"); break; case ERROR_INCLUDES_CIRCULAR_REFERENCE: snprintf(buffer, buffer_size, "include circular reference"); break; case ERROR_INCLUDE_DEPTH_EXCEEDED: snprintf(buffer, buffer_size, "too many levels of included rules"); break; case ERROR_LOOP_NESTING_LIMIT_EXCEEDED: snprintf(buffer, buffer_size, "loop nesting limit exceeded"); break; case ERROR_NESTED_FOR_OF_LOOP: snprintf( buffer, buffer_size, "'for of ' loops can't be nested"); break; case ERROR_UNKNOWN_MODULE: snprintf( buffer, buffer_size, "unknown module \"%s\"", compiler->last_error_extra_info); break; case ERROR_INVALID_MODULE_NAME: snprintf( buffer, buffer_size, "invalid module name \"%s\"", compiler->last_error_extra_info); break; case ERROR_DUPLICATED_STRUCTURE_MEMBER: snprintf(buffer, buffer_size, "duplicated structure member"); break; case ERROR_WRONG_ARGUMENTS: snprintf( buffer, buffer_size, "wrong arguments for function \"%s\"", compiler->last_error_extra_info); break; case ERROR_WRONG_RETURN_TYPE: snprintf(buffer, buffer_size, "wrong return type for overloaded function"); break; case ERROR_INVALID_HEX_STRING: case ERROR_INVALID_REGULAR_EXPRESSION: case ERROR_SYNTAX_ERROR: case ERROR_WRONG_TYPE: snprintf(buffer, buffer_size, "%s", compiler->last_error_extra_info); break; case ERROR_INTERNAL_FATAL_ERROR: snprintf(buffer, buffer_size, "internal fatal error"); break; case ERROR_DIVISION_BY_ZERO: snprintf(buffer, buffer_size, "division by zero"); break; case ERROR_REGULAR_EXPRESSION_TOO_LARGE: snprintf(buffer, buffer_size, "regular expression is too large"); break; case ERROR_REGULAR_EXPRESSION_TOO_COMPLEX: snprintf(buffer, buffer_size, "regular expression is too complex"); break; case ERROR_TOO_MANY_STRINGS: yr_get_configuration(YR_CONFIG_MAX_STRINGS_PER_RULE, &max_strings_per_rule); snprintf( buffer, buffer_size, "too many strings in rule \"%s\" (limit: %d)", compiler->last_error_extra_info, max_strings_per_rule); break; case ERROR_INTEGER_OVERFLOW: snprintf( buffer, buffer_size, "integer overflow in \"%s\"", compiler->last_error_extra_info); break; case ERROR_COULD_NOT_READ_FILE: snprintf(buffer, buffer_size, "could not read file"); break; case ERROR_INVALID_MODIFIER: snprintf(buffer, buffer_size, "%s", compiler->last_error_extra_info); break; case ERROR_DUPLICATED_MODIFIER: snprintf(buffer, buffer_size, "duplicated modifier"); break; } return buffer; } yara-4.1.3/libyara/crypto.h000066400000000000000000000113021413423160300155610ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_CRYPTO_H #define YR_CRYPTO_H #define YR_MD5_LEN 16 #define YR_SHA1_LEN 20 #define YR_SHA256_LEN 32 #if defined(HAVE_LIBCRYPTO) #include #include #include typedef MD5_CTX yr_md5_ctx; typedef SHA_CTX yr_sha1_ctx; typedef SHA256_CTX yr_sha256_ctx; #define yr_md5_init(ctx) MD5_Init(ctx) #define yr_md5_update(ctx, data, len) MD5_Update(ctx, data, len) #define yr_md5_final(digest, ctx) MD5_Final(digest, ctx) #define yr_sha1_init(ctx) SHA1_Init(ctx) #define yr_sha1_update(ctx, data, len) SHA1_Update(ctx, data, len) #define yr_sha1_final(digest, ctx) SHA1_Final(digest, ctx) #define yr_sha256_init(ctx) SHA256_Init(ctx) #define yr_sha256_update(ctx, data, len) SHA256_Update(ctx, data, len) #define yr_sha256_final(digest, ctx) SHA256_Final(digest, ctx) #elif defined(HAVE_WINCRYPT_H) #include #include extern HCRYPTPROV yr_cryptprov; typedef HCRYPTHASH yr_md5_ctx; typedef HCRYPTHASH yr_sha1_ctx; typedef HCRYPTHASH yr_sha256_ctx; #define yr_md5_init(ctx) CryptCreateHash(yr_cryptprov, CALG_MD5, 0, 0, ctx) #define yr_md5_update(ctx, data, len) \ CryptHashData(*ctx, (const BYTE*) data, len, 0) #define yr_md5_final(digest, ctx) \ { \ DWORD len = YR_MD5_LEN; \ CryptGetHashParam(*ctx, HP_HASHVAL, digest, &len, 0); \ CryptDestroyHash(*ctx); \ } #define yr_sha1_init(ctx) CryptCreateHash(yr_cryptprov, CALG_SHA1, 0, 0, ctx) #define yr_sha1_update(ctx, data, len) \ CryptHashData(*ctx, (const BYTE*) data, len, 0) #define yr_sha1_final(digest, ctx) \ { \ DWORD len = YR_SHA1_LEN; \ CryptGetHashParam(*ctx, HP_HASHVAL, digest, &len, 0); \ CryptDestroyHash(*ctx); \ } #define yr_sha256_init(ctx) \ CryptCreateHash(yr_cryptprov, CALG_SHA_256, 0, 0, ctx) #define yr_sha256_update(ctx, data, len) \ CryptHashData(*ctx, (const BYTE*) data, len, 0) #define yr_sha256_final(digest, ctx) \ { \ DWORD len = YR_SHA256_LEN; \ CryptGetHashParam(*ctx, HP_HASHVAL, digest, &len, 0); \ CryptDestroyHash(*ctx); \ } #elif defined(HAVE_COMMONCRYPTO_COMMONCRYPTO_H) #include typedef CC_MD5_CTX yr_md5_ctx; typedef CC_SHA1_CTX yr_sha1_ctx; typedef CC_SHA256_CTX yr_sha256_ctx; #define yr_md5_init(ctx) CC_MD5_Init(ctx) #define yr_md5_update(ctx, data, len) CC_MD5_Update(ctx, data, len) #define yr_md5_final(digest, ctx) CC_MD5_Final(digest, ctx) #define yr_sha1_init(ctx) CC_SHA1_Init(ctx) #define yr_sha1_update(ctx, data, len) CC_SHA1_Update(ctx, data, len) #define yr_sha1_final(digest, ctx) CC_SHA1_Final(digest, ctx) #define yr_sha256_init(ctx) CC_SHA256_Init(ctx) #define yr_sha256_update(ctx, data, len) CC_SHA256_Update(ctx, data, len) #define yr_sha256_final(digest, ctx) CC_SHA256_Final(digest, ctx) #endif #endif yara-4.1.3/libyara/endian.c000066400000000000000000000041641413423160300155020ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include uint16_t _yr_bswap16(uint16_t x) { return (x >> 8 | x << 8); } uint32_t _yr_bswap32(uint32_t x) { return ( (((x) &0xff000000) >> 24) | (((x) &0x00ff0000) >> 8) | (((x) &0x0000ff00) << 8) | (((x) &0x000000ff) << 24)); } uint64_t _yr_bswap64(uint64_t x) { return ( (((x) &0xff00000000000000ull) >> 56) | (((x) &0x00ff000000000000ull) >> 40) | (((x) &0x0000ff0000000000ull) >> 24) | (((x) &0x000000ff00000000ull) >> 8) | (((x) &0x00000000ff000000ull) << 8) | (((x) &0x0000000000ff0000ull) << 24) | (((x) &0x000000000000ff00ull) << 40) | (((x) &0x00000000000000ffull) << 56)); } yara-4.1.3/libyara/exception.h000066400000000000000000000201551413423160300162450ustar00rootroot00000000000000/* Copyright (c) 2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_EXCEPTION_H #define YR_EXCEPTION_H #include #include #if _WIN32 || __CYGWIN__ #include // If compiling with Microsoft's compiler use structered exception handling. #ifdef _MSC_VER #include static LONG CALLBACK exception_handler(PEXCEPTION_POINTERS ExceptionInfo) { switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_IN_PAGE_ERROR: case EXCEPTION_ACCESS_VIOLATION: return EXCEPTION_EXECUTE_HANDLER; } return EXCEPTION_CONTINUE_SEARCH; } #define YR_TRYCATCH(_do_, _try_clause_, _catch_clause_) \ do \ { \ if (_do_) \ { \ __try \ { \ _try_clause_ \ } \ __except (exception_handler(GetExceptionInformation())) \ { \ _catch_clause_ \ } \ } \ else \ { \ _try_clause_ \ } \ } while (0) #else // If not compiling with Microsoft's compiler use vectored exception handling. #include static LONG CALLBACK exception_handler(PEXCEPTION_POINTERS ExceptionInfo) { jmp_buf* jb_ptr; switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_IN_PAGE_ERROR: case EXCEPTION_ACCESS_VIOLATION: jb_ptr = (jmp_buf*) yr_thread_storage_get_value(&yr_trycatch_trampoline_tls); if (jb_ptr != NULL) longjmp(*jb_ptr, 1); } return EXCEPTION_CONTINUE_SEARCH; } #define YR_TRYCATCH(_do_, _try_clause_, _catch_clause_) \ do \ { \ if (_do_) \ { \ jmp_buf jb; \ /* Store pointer to sigjmp_buf in TLS */ \ yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, &jb); \ HANDLE exh = AddVectoredExceptionHandler(1, exception_handler); \ if (setjmp(jb) == 0) \ { \ _try_clause_ \ } \ else \ { \ _catch_clause_ \ } \ RemoveVectoredExceptionHandler(exh); \ yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, NULL); \ } \ else \ { \ _try_clause_ \ } \ } while (0) #endif #else #include #include #include static void exception_handler(int sig) { if (sig == SIGBUS || sig == SIGSEGV) { jmp_buf* jb_ptr = (jmp_buf*) yr_thread_storage_get_value(&yr_trycatch_trampoline_tls); if (jb_ptr != NULL) siglongjmp(*jb_ptr, 1); } } typedef struct sigaction sa; #define YR_TRYCATCH(_do_, _try_clause_, _catch_clause_) \ do \ { \ if (_do_) \ { \ struct sigaction old_sigbus_act; \ struct sigaction old_sigsegv_act; \ struct sigaction act; \ sigjmp_buf jb; \ /* Store pointer to sigjmp_buf in TLS */ \ yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, &jb); \ /* Set exception handler for SIGBUS and SIGSEGV*/ \ act.sa_handler = exception_handler; \ act.sa_flags = 0; /* SA_ONSTACK? */ \ sigfillset(&act.sa_mask); \ sigaction(SIGBUS, &act, &old_sigbus_act); \ sigaction(SIGSEGV, &act, &old_sigsegv_act); \ if (sigsetjmp(jb, 1) == 0) \ { \ _try_clause_ \ } \ else \ { \ _catch_clause_ \ } \ /* Stop capturing SIGBUS and SIGSEGV */ \ sigaction(SIGBUS, &old_sigbus_act, NULL); \ sigaction(SIGSEGV, &old_sigsegv_act, NULL); \ yr_thread_storage_set_value(&yr_trycatch_trampoline_tls, NULL); \ } \ else \ { \ _try_clause_ \ } \ } while (0) #endif #endif yara-4.1.3/libyara/exec.c000066400000000000000000001417101413423160300151670ustar00rootroot00000000000000/* Copyright (c) 2013-2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_SIZE YR_MAX_LOOP_NESTING*(YR_MAX_LOOP_VARS + YR_INTERNAL_LOOP_VARS) #define push(x) \ if (stack.sp < stack.capacity) \ { \ stack.items[stack.sp++] = (x); \ } \ else \ { \ result = ERROR_EXEC_STACK_OVERFLOW; \ stop = true; \ break; \ } #define pop(x) \ { \ assert(stack.sp > 0); \ x = stack.items[--stack.sp]; \ } #define is_undef(x) IS_UNDEFINED((x).i) #define ensure_defined(x) \ if (is_undef(x)) \ { \ r1.i = YR_UNDEFINED; \ push(r1); \ break; \ } #define ensure_within_mem(x) \ if (x < 0 || x >= MEM_SIZE) \ { \ stop = true; \ result = ERROR_INTERNAL_FATAL_ERROR; \ break; \ } // Make sure that the string pointer is within the rules arena. #define ensure_within_rules_arena(x) \ { \ YR_ARENA_REF ref; \ if (yr_arena_ptr_to_ref(context->rules->arena, x, &ref) == 0) \ { \ stop = true; \ result = ERROR_INTERNAL_FATAL_ERROR; \ break; \ } \ } #define check_object_canary(o) \ if (o->canary != context->canary) \ { \ stop = true; \ result = ERROR_INTERNAL_FATAL_ERROR; \ break; \ } #define little_endian_uint8_t(x) (x) #define little_endian_int8_t(x) (x) #define little_endian_uint16_t(x) yr_le16toh(x) #define little_endian_int16_t(x) yr_le16toh(x) #define little_endian_uint32_t(x) yr_le32toh(x) #define little_endian_int32_t(x) yr_le32toh(x) #define big_endian_uint8_t(x) (x) #define big_endian_int8_t(x) (x) #define big_endian_uint16_t(x) yr_be16toh(x) #define big_endian_int16_t(x) yr_be16toh(x) #define big_endian_uint32_t(x) yr_be32toh(x) #define big_endian_int32_t(x) yr_be32toh(x) #define function_read(type, endianess) \ int64_t read_##type##_##endianess( \ YR_MEMORY_BLOCK_ITERATOR* iterator, size_t offset) \ { \ YR_MEMORY_BLOCK* block = iterator->first(iterator); \ while (block != NULL) \ { \ if (offset >= block->base && block->size >= sizeof(type) && \ offset <= block->base + block->size - sizeof(type)) \ { \ type result; \ const uint8_t* data = block->fetch_data(block); \ if (data == NULL) \ return YR_UNDEFINED; \ result = *(type*) (data + offset - block->base); \ result = endianess##_##type(result); \ return result; \ } \ block = iterator->next(iterator); \ } \ return YR_UNDEFINED; \ }; function_read(uint8_t, little_endian); function_read(uint16_t, little_endian); function_read(uint32_t, little_endian); function_read(int8_t, little_endian); function_read(int16_t, little_endian); function_read(int32_t, little_endian); function_read(uint8_t, big_endian); function_read(uint16_t, big_endian); function_read(uint32_t, big_endian); function_read(int8_t, big_endian); function_read(int16_t, big_endian); function_read(int32_t, big_endian); static const uint8_t* jmp_if(int condition, const uint8_t* ip) { size_t off; if (condition) { // The condition is true, the instruction pointer is incremented in the // amount specified by the jump's offset. The offset is relative to the // jump opcode, but now the instruction pointer is pointing past the opcode // that's why we decrement the offset by 1. off = *(int32_t*) (ip) -1; } else { // The condition is false, the execution flow proceeds with the instruction // right after the jump. off = sizeof(int32_t); } return ip + off; } static int iter_array_next(YR_ITERATOR* self, YR_VALUE_STACK* stack) { YR_OBJECT* obj; // Check that there's two available slots in the stack, one for the next // item returned by the iterator and another one for the boolean that // indicates if there are more items. if (stack->sp + 1 >= stack->capacity) return ERROR_EXEC_STACK_OVERFLOW; if (self->array_it.index < yr_object_array_length(self->array_it.array)) { // Push the false value that indicates that the iterator is not exhausted. stack->items[stack->sp++].i = 0; obj = yr_object_array_get_item( self->array_it.array, 0, self->array_it.index); if (obj != NULL) stack->items[stack->sp++].o = obj; else stack->items[stack->sp++].i = YR_UNDEFINED; self->array_it.index++; } else { // Push true for indicating the iterator has been exhausted. stack->items[stack->sp++].i = 1; // Push YR_UNDEFINED as a placeholder for the next item. stack->items[stack->sp++].i = YR_UNDEFINED; } return ERROR_SUCCESS; } static int iter_dict_next(YR_ITERATOR* self, YR_VALUE_STACK* stack) { YR_DICTIONARY_ITEMS* items = object_as_dictionary(self->dict_it.dict)->items; // Check that there's three available slots in the stack, two for the next // item returned by the iterator and its key, and another one for the boolean // that indicates if there are more items. if (stack->sp + 2 >= stack->capacity) return ERROR_EXEC_STACK_OVERFLOW; // If the dictionary has no items or the iterator reached the last item, abort // the iteration, if not push the next key and value. if (items == NULL || self->dict_it.index == items->used) { // Push true for indicating the iterator has been exhausted. stack->items[stack->sp++].i = 1; // Push YR_UNDEFINED as a placeholder for the next key and value. stack->items[stack->sp++].i = YR_UNDEFINED; stack->items[stack->sp++].i = YR_UNDEFINED; } else { // Push the false value that indicates that the iterator is not exhausted. stack->items[stack->sp++].i = 0; if (items->objects[self->dict_it.index].obj != NULL) { stack->items[stack->sp++].o = items->objects[self->dict_it.index].obj; stack->items[stack->sp++].p = items->objects[self->dict_it.index].key; } else { stack->items[stack->sp++].i = YR_UNDEFINED; stack->items[stack->sp++].i = YR_UNDEFINED; } self->dict_it.index++; } return ERROR_SUCCESS; } static int iter_int_range_next(YR_ITERATOR* self, YR_VALUE_STACK* stack) { // Check that there's two available slots in the stack, one for the next // item returned by the iterator and another one for the boolean that // indicates if there are more items. if (stack->sp + 1 >= stack->capacity) return ERROR_EXEC_STACK_OVERFLOW; if (!IS_UNDEFINED(self->int_range_it.next) && !IS_UNDEFINED(self->int_range_it.last) && self->int_range_it.next <= self->int_range_it.last) { // Push the false value that indicates that the iterator is not exhausted. stack->items[stack->sp++].i = 0; stack->items[stack->sp++].i = self->int_range_it.next; self->int_range_it.next++; } else { // Push true for indicating the iterator has been exhausted. stack->items[stack->sp++].i = 1; // Push YR_UNDEFINED as a placeholder for the next item. stack->items[stack->sp++].i = YR_UNDEFINED; } return ERROR_SUCCESS; } static int iter_int_enum_next(YR_ITERATOR* self, YR_VALUE_STACK* stack) { // Check that there's two available slots in the stack, one for the next // item returned by the iterator and another one for the boolean that // indicates if there are more items. if (stack->sp + 1 >= stack->capacity) return ERROR_EXEC_STACK_OVERFLOW; if (!IS_UNDEFINED(self->int_enum_it.next) && !IS_UNDEFINED(self->int_enum_it.count) && self->int_enum_it.next < self->int_enum_it.count) { // Push the false value that indicates that the iterator is not exhausted. stack->items[stack->sp++].i = 0; stack->items[stack->sp++].i = self->int_enum_it.items[self->int_enum_it.next]; self->int_enum_it.next++; } else { // Push true for indicating the iterator has been exhausted. stack->items[stack->sp++].i = 1; // Push YR_UNDEFINED as a placeholder for the next item. stack->items[stack->sp++].i = YR_UNDEFINED; } return ERROR_SUCCESS; } int yr_execute_code(YR_SCAN_CONTEXT* context) { YR_DEBUG_FPRINTF(2, stderr, "+ %s() {\n", __FUNCTION__); const uint8_t* ip = context->rules->code_start; YR_VALUE mem[MEM_SIZE]; YR_VALUE args[YR_MAX_FUNCTION_ARGS]; YR_VALUE r1; YR_VALUE r2; YR_VALUE r3; YR_VALUE r4; YR_VALUE_STACK stack; uint64_t elapsed_time; #ifdef YR_PROFILING_ENABLED uint64_t start_time; #endif uint32_t current_rule_idx = 0; YR_RULE* current_rule = NULL; YR_RULE* rule; YR_MATCH* match; YR_OBJECT_FUNCTION* function; YR_OBJECT** obj_ptr; YR_ARENA* obj_arena; YR_NOTEBOOK* it_notebook; char* identifier; char* args_fmt; int found; int count; int result = ERROR_SUCCESS; int cycle = 0; int obj_count = 0; bool stop = false; uint8_t opcode; yr_get_configuration(YR_CONFIG_STACK_SIZE, (void*) &stack.capacity); stack.sp = 0; stack.items = (YR_VALUE*) yr_malloc(stack.capacity * sizeof(YR_VALUE)); if (stack.items == NULL) return ERROR_INSUFFICIENT_MEMORY; FAIL_ON_ERROR_WITH_CLEANUP( yr_arena_create(1, 512 * sizeof(YR_OBJECT*), &obj_arena), yr_free(stack.items)); FAIL_ON_ERROR_WITH_CLEANUP( yr_notebook_create(512 * sizeof(YR_ITERATOR), &it_notebook), yr_arena_release(obj_arena); yr_free(stack.items)); #ifdef YR_PROFILING_ENABLED start_time = yr_stopwatch_elapsed_ns(&context->stopwatch); #endif #if YR_PARANOID_EXEC memset(mem, 0, MEM_SIZE * sizeof(mem[0])); #endif while (!stop) { // Read the opcode from the address indicated by the instruction pointer. opcode = *ip; // Advance the instruction pointer, which now points past the opcode. ip++; switch (opcode) { case OP_NOP: YR_DEBUG_FPRINTF(2, stderr, "- case OP_NOP: // %s()\n", __FUNCTION__); break; case OP_HALT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_HALT: // %s()\n", __FUNCTION__); assert(stack.sp == 0); // When HALT is reached the stack should be empty. stop = true; break; case OP_ITER_START_ARRAY: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ITER_START_ARRAY: // %s()\n", __FUNCTION__); r2.p = yr_notebook_alloc(it_notebook, sizeof(YR_ITERATOR)); if (r2.p == NULL) { result = ERROR_INSUFFICIENT_MEMORY; } else { pop(r1); r2.it->array_it.array = r1.o; r2.it->array_it.index = 0; r2.it->next = iter_array_next; push(r2); } stop = (result != ERROR_SUCCESS); break; case OP_ITER_START_DICT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ITER_START_DICT: // %s()\n", __FUNCTION__); r2.p = yr_notebook_alloc(it_notebook, sizeof(YR_ITERATOR)); if (r2.p == NULL) { result = ERROR_INSUFFICIENT_MEMORY; } else { pop(r1); r2.it->dict_it.dict = r1.o; r2.it->dict_it.index = 0; r2.it->next = iter_dict_next; push(r2); } stop = (result != ERROR_SUCCESS); break; case OP_ITER_START_INT_RANGE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ITER_START_INT_RANGE: // %s()\n", __FUNCTION__); // Creates an iterator for an integer range. The higher bound of the // range is at the top of the stack followed by the lower bound. r3.p = yr_notebook_alloc(it_notebook, sizeof(YR_ITERATOR)); if (r3.p == NULL) { result = ERROR_INSUFFICIENT_MEMORY; } else { pop(r2); pop(r1); r3.it->int_range_it.next = r1.i; r3.it->int_range_it.last = r2.i; r3.it->next = iter_int_range_next; push(r3); } stop = (result != ERROR_SUCCESS); break; case OP_ITER_START_INT_ENUM: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ITER_START_INT_ENUM: // %s()\n", __FUNCTION__); // Creates an iterator for an integer enumeration. The number of items // in the enumeration is at the top of the stack, followed by the // items in reverse order. pop(r1); r3.p = yr_notebook_alloc( it_notebook, sizeof(YR_ITERATOR) + sizeof(uint64_t) * (size_t) r1.i); if (r3.p == NULL) { result = ERROR_INSUFFICIENT_MEMORY; } else { r3.it->int_enum_it.count = r1.i; r3.it->int_enum_it.next = 0; r3.it->next = iter_int_enum_next; for (int64_t i = r1.i; i > 0; i--) { pop(r2); r3.it->int_enum_it.items[i - 1] = r2.i; } push(r3); } stop = (result != ERROR_SUCCESS); break; case OP_ITER_NEXT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ITER_NEXT: // %s()\n", __FUNCTION__); // Loads the iterator in r1, but leaves the iterator in the stack. pop(r1); push(r1); // The iterator's next function is responsible for pushing the next // item in the stack, and a boolean indicating if there are more items // to retrieve. The boolean will be at the top of the stack after // calling "next". result = r1.it->next(r1.it, &stack); stop = (result != ERROR_SUCCESS); break; case OP_PUSH: YR_DEBUG_FPRINTF(2, stderr, "- case OP_PUSH: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); push(r1); break; case OP_PUSH_8: r1.i = *ip; YR_DEBUG_FPRINTF( 2, stderr, "- case OP_PUSH_8: r1.i=%" PRId64 " // %s()\n", r1.i, __FUNCTION__); ip += sizeof(uint8_t); push(r1); break; case OP_PUSH_16: r1.i = *(uint16_t*) (ip); YR_DEBUG_FPRINTF( 2, stderr, "- case OP_PUSH_16: r1.i=%" PRId64 " // %s()\n", r1.i, __FUNCTION__); ip += sizeof(uint16_t); push(r1); break; case OP_PUSH_32: r1.i = *(uint32_t*) (ip); YR_DEBUG_FPRINTF( 2, stderr, "- case OP_PUSH_32: r1.i=%" PRId64 " // %s()\n", r1.i, __FUNCTION__); ip += sizeof(uint32_t); push(r1); break; case OP_PUSH_U: YR_DEBUG_FPRINTF(2, stderr, "- case OP_PUSH_U: // %s()\n", __FUNCTION__); r1.i = YR_UNDEFINED; push(r1); break; case OP_POP: YR_DEBUG_FPRINTF(2, stderr, "- case OP_POP: // %s()\n", __FUNCTION__); pop(r1); break; case OP_CLEAR_M: YR_DEBUG_FPRINTF(2, stderr, "- case OP_CLEAR_M: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif mem[r1.i].i = 0; break; case OP_ADD_M: YR_DEBUG_FPRINTF(2, stderr, "- case OP_ADD_M: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); if (!is_undef(r2)) mem[r1.i].i += r2.i; break; case OP_INCR_M: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INCR_M: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif mem[r1.i].i++; break; case OP_PUSH_M: YR_DEBUG_FPRINTF(2, stderr, "- case OP_PUSH_M: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif r1 = mem[r1.i]; push(r1); break; case OP_POP_M: YR_DEBUG_FPRINTF(2, stderr, "- case OP_POP_M: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); mem[r1.i] = r2; break; case OP_SET_M: YR_DEBUG_FPRINTF(2, stderr, "- case OP_SET_M: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); push(r2); if (!is_undef(r2)) mem[r1.i] = r2; break; case OP_SWAPUNDEF: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_SWAPUNDEF: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_mem(r1.i); #endif pop(r2); if (is_undef(r2)) { r1 = mem[r1.i]; push(r1); } else { push(r2); } break; case OP_JNUNDEF: // Jump if the top the stack is not undefined without modifying the stack. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JNUNDEF: // %s()\n", __FUNCTION__); pop(r1); push(r1); ip = jmp_if(!is_undef(r1), ip); break; case OP_JUNDEF_P: // Removes a value from the top of the stack and jump if the value is not // undefined. YR_DEBUG_FPRINTF( 2, stderr, "- case OP_JUNDEF_P: // %s()\n", __FUNCTION__); pop(r1); ip = jmp_if(is_undef(r1), ip); break; case OP_JL_P: // Pops two values A and B from the stack and jump if A < B. B is popped // first, and then A. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JL_P: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ip = jmp_if(r1.i < r2.i, ip); break; case OP_JLE_P: // Pops two values A and B from the stack and jump if A <= B. B is popped // first, and then A. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JLE_P: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ip = jmp_if(r1.i <= r2.i, ip); break; case OP_JTRUE: // Jump if the top of the stack is true without modifying the stack. If // the top of the stack is undefined the jump is not taken. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JTRUE: // %s()\n", __FUNCTION__); pop(r1); push(r1); ip = jmp_if(!is_undef(r1) && r1.i, ip); break; case OP_JTRUE_P: // Removes a value from the stack and jump if it is true. If the value // is undefined the jump is not taken. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JTRUE_P: // %s()\n", __FUNCTION__); pop(r1); ip = jmp_if(!is_undef(r1) && r1.i, ip); break; case OP_JFALSE: // Jump if the top of the stack is false without modifying the stack. If // the top of the stack is undefined the jump is not taken. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JFALSE: // %s()\n", __FUNCTION__); pop(r1); push(r1); ip = jmp_if(!is_undef(r1) && !r1.i, ip); break; case OP_JFALSE_P: // Removes a value from the stack and jump if it is false. If the value // is undefined the jump is not taken. YR_DEBUG_FPRINTF( 2, stderr, "- case OP_JFALSE_P: // %s()\n", __FUNCTION__); pop(r1); ip = jmp_if(!is_undef(r1) && !r1.i, ip); break; case OP_JZ: // Jump if the value at the top of the stack is 0 without modifying the // stack. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JZ: // %s()\n", __FUNCTION__); pop(r1); push(r1); ip = jmp_if(r1.i == 0, ip); break; case OP_JZ_P: // Removes a value from the stack and jump if the value is 0. YR_DEBUG_FPRINTF(2, stderr, "- case OP_JZ_P: // %s()\n", __FUNCTION__); pop(r1); ip = jmp_if(r1.i == 0, ip); break; case OP_AND: YR_DEBUG_FPRINTF(2, stderr, "- case OP_AND: // %s()\n", __FUNCTION__); pop(r2); pop(r1); if (is_undef(r1)) r1.i = 0; if (is_undef(r2)) r2.i = 0; r1.i = r1.i && r2.i; push(r1); break; case OP_OR: YR_DEBUG_FPRINTF(2, stderr, "- case OP_OR: // %s()\n", __FUNCTION__); pop(r2); pop(r1); if (is_undef(r1)) r1.i = 0; if (is_undef(r2)) r2.i = 0; r1.i = r1.i || r2.i; push(r1); break; case OP_NOT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_NOT: // %s()\n", __FUNCTION__); pop(r1); if (is_undef(r1)) r1.i = YR_UNDEFINED; else r1.i = !r1.i; push(r1); break; case OP_MOD: YR_DEBUG_FPRINTF(2, stderr, "- case OP_MOD: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i != 0) r1.i = r1.i % r2.i; else r1.i = YR_UNDEFINED; push(r1); break; case OP_SHR: YR_DEBUG_FPRINTF(2, stderr, "- case OP_SHR: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i < 0) r1.i = YR_UNDEFINED; else if (r2.i < 64) r1.i = r1.i >> r2.i; else r1.i = 0; push(r1); break; case OP_SHL: YR_DEBUG_FPRINTF(2, stderr, "- case OP_SHL: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i < 0) r1.i = YR_UNDEFINED; else if (r2.i < 64) r1.i = r1.i << r2.i; else r1.i = 0; push(r1); break; case OP_BITWISE_NOT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_BITWISE_NOT: // %s()\n", __FUNCTION__); pop(r1); ensure_defined(r1); r1.i = ~r1.i; push(r1); break; case OP_BITWISE_AND: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_BITWISE_AND: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i & r2.i; push(r1); break; case OP_BITWISE_OR: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_BITWISE_OR: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i | r2.i; push(r1); break; case OP_BITWISE_XOR: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_BITWISE_XOR: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i ^ r2.i; push(r1); break; case OP_PUSH_RULE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_PUSH_RULE: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); rule = &context->rules->rules_table[r1.i]; if (RULE_IS_DISABLED(rule)) { r2.i = YR_UNDEFINED; } else { if yr_bitmask_is_set (context->rule_matches_flags, r1.i) r2.i = 1; else r2.i = 0; } push(r2); break; case OP_INIT_RULE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_INIT_RULE: // %s()\n", __FUNCTION__); // After the opcode there's an int32_t corresponding to the jump's // offset and an uint32_t corresponding to the rule's index. current_rule_idx = *(uint32_t*) (ip + sizeof(int32_t)); assert(current_rule_idx < context->rules->num_rules); current_rule = &context->rules->rules_table[current_rule_idx]; // If the rule is disabled let's skip its code. ip = jmp_if(RULE_IS_DISABLED(current_rule), ip); // Skip the bytes corresponding to the rule's index, but only if not // taking the jump. if (!RULE_IS_DISABLED(current_rule)) ip += sizeof(uint32_t); break; case OP_MATCH_RULE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_MATCH_RULE: // %s()\n", __FUNCTION__); pop(r1); memcpy(&r2.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); rule = &context->rules->rules_table[r2.i]; #if YR_PARANOID_EXEC ensure_within_rules_arena(rule); #endif if (!is_undef(r1) && r1.i) yr_bitmask_set(context->rule_matches_flags, r2.i); else if (RULE_IS_GLOBAL(rule)) yr_bitmask_set(context->ns_unsatisfied_flags, rule->ns->idx); #ifdef YR_PROFILING_ENABLED elapsed_time = yr_stopwatch_elapsed_ns(&context->stopwatch); context->profiling_info[r2.i].exec_time += (elapsed_time - start_time); start_time = elapsed_time; #endif assert(stack.sp == 0); // at this point the stack should be empty. break; case OP_OBJ_LOAD: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_OBJ_LOAD: // %s()\n", __FUNCTION__); identifier = *(char**) (ip); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_rules_arena(identifier); #endif r1.o = (YR_OBJECT*) yr_hash_table_lookup( context->objects_table, identifier, NULL); assert(r1.o != NULL); push(r1); break; case OP_OBJ_FIELD: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_OBJ_FIELD: // %s()\n", __FUNCTION__); identifier = *(char**) (ip); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_rules_arena(identifier); #endif pop(r1); ensure_defined(r1); r1.o = yr_object_lookup_field(r1.o, identifier); if (r1.o == NULL) { result = ERROR_INVALID_FIELD_NAME; stop = true; break; } push(r1); break; case OP_OBJ_VALUE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_OBJ_VALUE: // %s()\n", __FUNCTION__); pop(r1); ensure_defined(r1); #if YR_PARANOID_EXEC check_object_canary(r1.o); #endif switch (r1.o->type) { case OBJECT_TYPE_INTEGER: r1.i = r1.o->value.i; break; case OBJECT_TYPE_FLOAT: if (isnan(r1.o->value.d)) r1.i = YR_UNDEFINED; else r1.d = r1.o->value.d; break; case OBJECT_TYPE_STRING: if (r1.o->value.ss == NULL) r1.i = YR_UNDEFINED; else r1.ss = r1.o->value.ss; break; default: assert(false); } push(r1); break; case OP_INDEX_ARRAY: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_INDEX_ARRAY: // %s()\n", __FUNCTION__); pop(r1); // index pop(r2); // array ensure_defined(r1); ensure_defined(r2); assert(r2.o->type == OBJECT_TYPE_ARRAY); #if YR_PARANOID_EXEC check_object_canary(r2.o); #endif r1.o = yr_object_array_get_item(r2.o, 0, (int) r1.i); if (r1.o == NULL) r1.i = YR_UNDEFINED; push(r1); break; case OP_LOOKUP_DICT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_LOOKUP_DICT: // %s()\n", __FUNCTION__); pop(r1); // key pop(r2); // dictionary ensure_defined(r1); ensure_defined(r2); assert(r2.o->type == OBJECT_TYPE_DICTIONARY); #if YR_PARANOID_EXEC check_object_canary(r2.o); #endif r1.o = yr_object_dict_get_item(r2.o, 0, r1.ss->c_string); if (r1.o == NULL) r1.i = YR_UNDEFINED; push(r1); break; case OP_CALL: YR_DEBUG_FPRINTF(2, stderr, "- case OP_CALL: // %s()\n", __FUNCTION__); args_fmt = *(char**) (ip); ip += sizeof(uint64_t); int i = (int) strlen(args_fmt); count = 0; #if YR_PARANOID_EXEC if (i > YR_MAX_FUNCTION_ARGS) { stop = true; result = ERROR_INTERNAL_FATAL_ERROR; break; } #endif // pop arguments from stack and copy them to args array while (i > 0) { pop(r1); if (is_undef(r1)) // count the number of undefined args count++; args[i - 1] = r1; i--; } pop(r2); ensure_defined(r2); #if YR_PARANOID_EXEC check_object_canary(r2.o); #endif if (count > 0) { // If there are undefined args, result for function call // is undefined as well. r1.i = YR_UNDEFINED; push(r1); break; } function = object_as_function(r2.o); result = ERROR_INTERNAL_FATAL_ERROR; for (int i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++) { if (function->prototypes[i].arguments_fmt == NULL) break; if (strcmp(function->prototypes[i].arguments_fmt, args_fmt) == 0) { result = function->prototypes[i].code(args, context, function); break; } } // If i == YR_MAX_OVERLOADED_FUNCTIONS at this point no matching // prototype was found, but this shouldn't happen. assert(i < YR_MAX_OVERLOADED_FUNCTIONS); // Make a copy of the returned object and push the copy into the stack, // function->return_obj can't be pushed because it can change in // subsequent calls to the same function. if (result == ERROR_SUCCESS) result = yr_object_copy(function->return_obj, &r1.o); // A pointer to the copied object is stored in a arena in order to // free the object before exiting yr_execute_code, obj_count tracks // the number of objects written. if (result == ERROR_SUCCESS) { result = yr_arena_write_data(obj_arena, 0, &r1.o, sizeof(r1.o), NULL); obj_count++; } stop = (result != ERROR_SUCCESS); push(r1); break; case OP_FOUND: pop(r1); r2.i = context->matches[r1.s->idx].tail != NULL ? 1 : 0; YR_DEBUG_FPRINTF( 2, stderr, "- case OP_FOUND: r2.i=%" PRId64 " // %s()\n", r2.i, __FUNCTION__); push(r2); break; case OP_FOUND_AT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_FOUND_AT: // %s()\n", __FUNCTION__); pop(r2); pop(r1); if (is_undef(r1)) { r1.i = 0; push(r1); break; } #if YR_PARANOID_EXEC ensure_within_rules_arena(r2.p); #endif match = context->matches[r2.s->idx].head; r3.i = false; while (match != NULL) { if (r1.i == match->base + match->offset) { r3.i = true; break; } if (r1.i < match->base + match->offset) break; match = match->next; } push(r3); break; case OP_FOUND_IN: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_FOUND_IN: // %s()\n", __FUNCTION__); pop(r3); pop(r2); pop(r1); ensure_defined(r1); ensure_defined(r2); #if YR_PARANOID_EXEC ensure_within_rules_arena(r3.p); #endif match = context->matches[r3.s->idx].head; r4.i = false; while (match != NULL && !r4.i) { if (match->base + match->offset >= r1.i && match->base + match->offset <= r2.i) { r4.i = true; } if (match->base + match->offset > r2.i) break; match = match->next; } push(r4); break; case OP_COUNT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_COUNT: // %s()\n", __FUNCTION__); pop(r1); #if YR_PARANOID_EXEC ensure_within_rules_arena(r1.p); #endif r2.i = context->matches[r1.s->idx].count; push(r2); break; case OP_OFFSET: YR_DEBUG_FPRINTF(2, stderr, "- case OP_OFFSET: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r1); #if YR_PARANOID_EXEC ensure_within_rules_arena(r2.p); #endif match = context->matches[r2.s->idx].head; i = 1; r3.i = YR_UNDEFINED; while (match != NULL && r3.i == YR_UNDEFINED) { if (r1.i == i) r3.i = match->base + match->offset; i++; match = match->next; } push(r3); break; case OP_LENGTH: YR_DEBUG_FPRINTF(2, stderr, "- case OP_LENGTH: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r1); #if YR_PARANOID_EXEC ensure_within_rules_arena(r2.p); #endif match = context->matches[r2.s->idx].head; i = 1; r3.i = YR_UNDEFINED; while (match != NULL && r3.i == YR_UNDEFINED) { if (r1.i == i) r3.i = match->match_length; i++; match = match->next; } push(r3); break; case OP_OF: YR_DEBUG_FPRINTF(2, stderr, "- case OP_OF: // %s()\n", __FUNCTION__); found = 0; count = 0; pop(r1); while (!is_undef(r1)) { if (context->matches[r1.s->idx].tail != NULL) { found++; } count++; pop(r1); } pop(r2); if (is_undef(r2)) r1.i = found >= count ? 1 : 0; else r1.i = found >= r2.i ? 1 : 0; push(r1); break; case OP_FILESIZE: r1.i = context->file_size; YR_DEBUG_FPRINTF( 2, stderr, "- case OP_FILESIZE: r1.i=%" PRId64 "%s // %s()\n", r1.i, r1.i == YR_UNDEFINED ? " AKA YR_UNDEFINED" : "", __FUNCTION__); push(r1); break; case OP_ENTRYPOINT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ENTRYPOINT: // %s()\n", __FUNCTION__); r1.i = context->entry_point; push(r1); break; case OP_INT8: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT8: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_int8_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT16: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT16: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_int16_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT32: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT32: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_int32_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT8: YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT8: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_uint8_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT16: YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT16: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_uint16_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT32: YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT32: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_uint32_t_little_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT8BE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT8BE: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_int8_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT16BE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT16BE: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_int16_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_INT32BE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT32BE: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_int32_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT8BE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_UINT8BE: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_uint8_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT16BE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_UINT16BE: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_uint16_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_UINT32BE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_UINT32BE: // %s()\n", __FUNCTION__); pop(r1); r1.i = read_uint32_t_big_endian(context->iterator, (size_t) r1.i); push(r1); break; case OP_IMPORT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_IMPORT: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC ensure_within_rules_arena(r1.p); #endif result = yr_modules_load((char*) r1.p, context); if (result != ERROR_SUCCESS) stop = true; break; case OP_MATCHES: YR_DEBUG_FPRINTF(2, stderr, "- case OP_MATCHES: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r1.ss->length == 0) { r1.i = false; push(r1); break; } result = yr_re_exec( context, (uint8_t*) r2.re->code, (uint8_t*) r1.ss->c_string, r1.ss->length, 0, r2.re->flags | RE_FLAGS_SCAN, NULL, NULL, &found); if (result != ERROR_SUCCESS) stop = true; r1.i = found >= 0; push(r1); break; case OP_INT_TO_DBL: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_INT_TO_DBL: // %s()\n", __FUNCTION__); memcpy(&r1.i, ip, sizeof(uint64_t)); ip += sizeof(uint64_t); #if YR_PARANOID_EXEC if (r1.i > stack.sp || stack.sp - r1.i >= stack.capacity) { stop = true; result = ERROR_INTERNAL_FATAL_ERROR; break; } #endif r2 = stack.items[stack.sp - r1.i]; if (is_undef(r2)) stack.items[stack.sp - r1.i].i = YR_UNDEFINED; else stack.items[stack.sp - r1.i].d = (double) r2.i; break; case OP_STR_TO_BOOL: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_TO_BOOL: // %s()\n", __FUNCTION__); pop(r1); ensure_defined(r1); r1.i = r1.ss->length > 0; push(r1); break; case OP_INT_EQ: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_EQ: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i == r2.i; push(r1); break; case OP_INT_NEQ: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_NEQ: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i != r2.i; push(r1); break; case OP_INT_LT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_LT: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i < r2.i; push(r1); break; case OP_INT_GT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_GT: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i > r2.i; push(r1); break; case OP_INT_LE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_LE: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i <= r2.i; push(r1); break; case OP_INT_GE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_GE: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i >= r2.i; push(r1); break; case OP_INT_ADD: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_ADD: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i + r2.i; push(r1); break; case OP_INT_SUB: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_SUB: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i - r2.i; push(r1); break; case OP_INT_MUL: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_MUL: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.i * r2.i; push(r1); break; case OP_INT_DIV: YR_DEBUG_FPRINTF(2, stderr, "- case OP_INT_DIV: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); if (r2.i != 0) r1.i = r1.i / r2.i; else r1.i = YR_UNDEFINED; push(r1); break; case OP_INT_MINUS: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_INT_MINUS: // %s()\n", __FUNCTION__); pop(r1); ensure_defined(r1); r1.i = -r1.i; push(r1); break; case OP_DBL_LT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_LT: // %s()\n", __FUNCTION__); pop(r2); pop(r1); if (is_undef(r1) || is_undef(r2)) r1.i = false; else r1.i = r1.d < r2.d; push(r1); break; case OP_DBL_GT: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_GT: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d > r2.d; push(r1); break; case OP_DBL_LE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_LE: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d <= r2.d; push(r1); break; case OP_DBL_GE: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_GE: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = r1.d >= r2.d; push(r1); break; case OP_DBL_EQ: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_EQ: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = fabs(r1.d - r2.d) < DBL_EPSILON; push(r1); break; case OP_DBL_NEQ: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_NEQ: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.i = fabs(r1.d - r2.d) >= DBL_EPSILON; push(r1); break; case OP_DBL_ADD: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_ADD: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d + r2.d; push(r1); break; case OP_DBL_SUB: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_SUB: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d - r2.d; push(r1); break; case OP_DBL_MUL: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_MUL: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d * r2.d; push(r1); break; case OP_DBL_DIV: YR_DEBUG_FPRINTF(2, stderr, "- case OP_DBL_DIV: // %s()\n", __FUNCTION__); pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); r1.d = r1.d / r2.d; push(r1); break; case OP_DBL_MINUS: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_DBL_MINUS: // %s()\n", __FUNCTION__); pop(r1); ensure_defined(r1); r1.d = -r1.d; push(r1); break; case OP_STR_EQ: case OP_STR_NEQ: case OP_STR_LT: case OP_STR_LE: case OP_STR_GT: case OP_STR_GE: pop(r2); pop(r1); ensure_defined(r2); ensure_defined(r1); switch (opcode) { case OP_STR_EQ: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_EQ: // %s()\n", __FUNCTION__); r1.i = (ss_compare(r1.ss, r2.ss) == 0); break; case OP_STR_NEQ: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_NEQ: // %s()\n", __FUNCTION__); r1.i = (ss_compare(r1.ss, r2.ss) != 0); break; case OP_STR_LT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_LT: // %s()\n", __FUNCTION__); r1.i = (ss_compare(r1.ss, r2.ss) < 0); break; case OP_STR_LE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_LE: // %s()\n", __FUNCTION__); r1.i = (ss_compare(r1.ss, r2.ss) <= 0); break; case OP_STR_GT: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_GT: // %s()\n", __FUNCTION__); r1.i = (ss_compare(r1.ss, r2.ss) > 0); break; case OP_STR_GE: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STR_GE: // %s()\n", __FUNCTION__); r1.i = (ss_compare(r1.ss, r2.ss) >= 0); break; } push(r1); break; case OP_CONTAINS: case OP_ICONTAINS: case OP_STARTSWITH: case OP_ISTARTSWITH: case OP_ENDSWITH: case OP_IENDSWITH: pop(r2); pop(r1); ensure_defined(r1); ensure_defined(r2); switch (opcode) { case OP_CONTAINS: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_CONTAINS: // %s()\n", __FUNCTION__); r1.i = ss_contains(r1.ss, r2.ss); break; case OP_ICONTAINS: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ICONTAINS: // %s()\n", __FUNCTION__); r1.i = ss_icontains(r1.ss, r2.ss); break; case OP_STARTSWITH: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_STARTSWITH: // %s()\n", __FUNCTION__); r1.i = ss_startswith(r1.ss, r2.ss); break; case OP_ISTARTSWITH: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ISTARTSWITH: // %s()\n", __FUNCTION__); r1.i = ss_istartswith(r1.ss, r2.ss); break; case OP_ENDSWITH: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_ENDSWITH: // %s()\n", __FUNCTION__); r1.i = ss_endswith(r1.ss, r2.ss); break; case OP_IENDSWITH: YR_DEBUG_FPRINTF( 2, stderr, "- case OP_IENDSWITH: // %s()\n", __FUNCTION__); r1.i = ss_iendswith(r1.ss, r2.ss); break; } push(r1); break; default: YR_DEBUG_FPRINTF( 2, stderr, "- case : // %s()\n", __FUNCTION__); // Unknown instruction, this shouldn't happen. assert(false); } // Check for timeout every 100 instruction cycles. If timeout == 0 it means // no timeout at all. if (context->timeout > 0ULL && ++cycle == 100) { elapsed_time = yr_stopwatch_elapsed_ns(&context->stopwatch); if (elapsed_time > context->timeout) { #ifdef YR_PROFILING_ENABLED context->profiling_info[current_rule_idx].exec_time += (elapsed_time - start_time); #endif result = ERROR_SCAN_TIMEOUT; stop = true; } cycle = 0; } } obj_ptr = yr_arena_get_ptr(obj_arena, 0, 0); for (int i = 0; i < obj_count; i++) yr_object_destroy(obj_ptr[i]); yr_arena_release(obj_arena); yr_notebook_destroy(it_notebook); yr_modules_unload_all(context); yr_free(stack.items); YR_DEBUG_FPRINTF( 2, stderr, "} = %d AKA %s // %s()\n", result, yr_debug_error_as_string(result), __FUNCTION__); return result; } yara-4.1.3/libyara/exefiles.c000066400000000000000000000267221413423160300160540ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #ifndef NULL #define NULL 0 #endif #ifndef MIN #define MIN(x, y) ((x < y) ? (x) : (y)) #endif PIMAGE_NT_HEADERS32 yr_get_pe_header( const uint8_t* buffer, size_t buffer_length) { PIMAGE_DOS_HEADER mz_header; PIMAGE_NT_HEADERS32 pe_header; size_t headers_size = 0; if (buffer_length < sizeof(IMAGE_DOS_HEADER)) return NULL; mz_header = (PIMAGE_DOS_HEADER) buffer; if (yr_le16toh(mz_header->e_magic) != IMAGE_DOS_SIGNATURE) return NULL; if ((int32_t) yr_le32toh(mz_header->e_lfanew) < 0) return NULL; headers_size = yr_le32toh(mz_header->e_lfanew) + sizeof(pe_header->Signature) + sizeof(IMAGE_FILE_HEADER); if (buffer_length < headers_size) return NULL; pe_header = (PIMAGE_NT_HEADERS32)(buffer + yr_le32toh(mz_header->e_lfanew)); headers_size += sizeof(IMAGE_OPTIONAL_HEADER32); if (yr_le32toh(pe_header->Signature) == IMAGE_NT_SIGNATURE && (yr_le16toh(pe_header->FileHeader.Machine) == IMAGE_FILE_MACHINE_I386 || yr_le16toh(pe_header->FileHeader.Machine) == IMAGE_FILE_MACHINE_AMD64) && buffer_length > headers_size) { return pe_header; } else { return NULL; } } uint64_t yr_pe_rva_to_offset( PIMAGE_NT_HEADERS32 pe_header, uint64_t rva, size_t buffer_length) { int i = 0; PIMAGE_SECTION_HEADER section; DWORD section_rva; DWORD section_offset; section = IMAGE_FIRST_SECTION(pe_header); section_rva = 0; section_offset = 0; while (i < MIN(yr_le16toh(pe_header->FileHeader.NumberOfSections), 60)) { if ((uint8_t*) section - (uint8_t*) pe_header + sizeof(IMAGE_SECTION_HEADER) < buffer_length) { if (rva >= section->VirtualAddress && section_rva <= yr_le32toh(section->VirtualAddress)) { section_rva = yr_le32toh(section->VirtualAddress); section_offset = yr_le32toh(section->PointerToRawData); } section++; i++; } else { return 0; } } return section_offset + (rva - section_rva); } int yr_get_elf_type(const uint8_t* buffer, size_t buffer_length) { elf_ident_t* elf_ident; if (buffer_length < sizeof(elf_ident_t)) return 0; elf_ident = (elf_ident_t*) buffer; if (yr_le32toh(elf_ident->magic) != ELF_MAGIC) { return 0; } switch (elf_ident->_class) { case ELF_CLASS_32: if (buffer_length < sizeof(elf32_header_t)) { return 0; } break; case ELF_CLASS_64: if (buffer_length < sizeof(elf64_header_t)) { return 0; } break; default: /* Unexpected class */ return 0; } return elf_ident->_class; } static uint64_t yr_elf_rva_to_offset_32( elf32_header_t* elf_header, uint64_t rva, size_t buffer_length) { // if the binary is an executable then prefer the program headers to resolve // the offset if (yr_le16toh(elf_header->type) == ELF_ET_EXEC) { int i; elf32_program_header_t* program; if (yr_le32toh(elf_header->ph_offset) == 0 || yr_le16toh(elf_header->ph_entry_count == 0)) return 0; // check to prevent integer wraps if (ULONG_MAX - yr_le16toh(elf_header->ph_entry_count) < sizeof(elf32_program_header_t) * yr_le16toh(elf_header->ph_entry_count)) return 0; // check that 'ph_offset' doesn't wrap when added to the // size of entries. if (ULONG_MAX - yr_le32toh(elf_header->ph_offset) < sizeof(elf32_program_header_t) * yr_le16toh(elf_header->ph_entry_count)) return 0; // ensure we don't exceed the buffer size if (yr_le32toh(elf_header->ph_offset) + sizeof(elf32_program_header_t) * yr_le16toh(elf_header->ph_entry_count) > buffer_length) return 0; program = (elf32_program_header_t*) ((uint8_t*) elf_header + yr_le32toh(elf_header->ph_offset)); for (i = 0; i < yr_le16toh(elf_header->ph_entry_count); i++) { if (rva >= yr_le32toh(program->virt_addr) && rva < yr_le32toh(program->virt_addr) + yr_le32toh(program->mem_size)) { return yr_le32toh(program->offset) + (rva - yr_le32toh(program->virt_addr)); } program++; } } else { int i; elf32_section_header_t* section; if (yr_le32toh(elf_header->sh_offset) == 0 || yr_le16toh(elf_header->sh_entry_count == 0)) return 0; // check to prevent integer wraps if (ULONG_MAX - yr_le16toh(elf_header->sh_entry_count) < sizeof(elf32_section_header_t) * yr_le16toh(elf_header->sh_entry_count)) return 0; // check that 'sh_offset' doesn't wrap when added to the // size of entries. if (ULONG_MAX - yr_le32toh(elf_header->sh_offset) < sizeof(elf32_section_header_t) * yr_le16toh(elf_header->sh_entry_count)) return 0; if (yr_le32toh(elf_header->sh_offset) + sizeof(elf32_section_header_t) * yr_le16toh(elf_header->sh_entry_count) > buffer_length) return 0; section = (elf32_section_header_t*) ((unsigned char*) elf_header + yr_le32toh(elf_header->sh_offset)); for (i = 0; i < yr_le16toh(elf_header->sh_entry_count); i++) { if (yr_le32toh(section->type) != ELF_SHT_NULL && yr_le32toh(section->type) != ELF_SHT_NOBITS && rva >= yr_le32toh(section->addr) && rva < yr_le32toh(section->addr) + yr_le32toh(section->size)) { // prevent integer wrapping with the return value if (ULONG_MAX - yr_le32toh(section->offset) < (rva - yr_le32toh(section->addr))) return 0; else return yr_le32toh(section->offset) + (rva - yr_le32toh(section->addr)); } section++; } } return 0; } static uint64_t yr_elf_rva_to_offset_64( elf64_header_t* elf_header, uint64_t rva, size_t buffer_length) { // if the binary is an executable then prefer the program headers to resolve // the offset if (yr_le16toh(elf_header->type) == ELF_ET_EXEC) { int i; elf64_program_header_t* program; if (yr_le64toh(elf_header->ph_offset) == 0 || yr_le16toh(elf_header->ph_entry_count == 0)) return 0; // check that 'ph_offset' doesn't wrap when added to the // size of entries. if (ULONG_MAX - yr_le64toh(elf_header->ph_offset) < sizeof(elf64_program_header_t) * yr_le16toh(elf_header->ph_entry_count)) return 0; // ensure we don't exceed the buffer size if (yr_le64toh(elf_header->ph_offset) + sizeof(elf64_program_header_t) * yr_le16toh(elf_header->ph_entry_count) > buffer_length) return 0; program = (elf64_program_header_t*) ((uint8_t*) elf_header + yr_le64toh(elf_header->ph_offset)); for (i = 0; i < yr_le16toh(elf_header->ph_entry_count); i++) { if (rva >= yr_le64toh(program->virt_addr) && rva < yr_le64toh(program->virt_addr) + yr_le64toh(program->mem_size)) { return yr_le64toh(program->offset) + (rva - yr_le64toh(program->virt_addr)); } program++; } } else { int i; elf64_section_header_t* section; if (yr_le64toh(elf_header->sh_offset) == 0 || yr_le16toh(elf_header->sh_entry_count) == 0) return 0; // check that 'sh_offset' doesn't wrap when added to the // size of entries. if (ULONG_MAX - yr_le64toh(elf_header->sh_offset) < sizeof(elf64_section_header_t) * yr_le16toh(elf_header->sh_entry_count)) return 0; if (yr_le64toh(elf_header->sh_offset) + sizeof(elf64_section_header_t) * yr_le16toh(elf_header->sh_entry_count) > buffer_length) return 0; section = (elf64_section_header_t*) ((uint8_t*) elf_header + yr_le64toh(elf_header->sh_offset)); for (i = 0; i < yr_le16toh(elf_header->sh_entry_count); i++) { if (yr_le32toh(section->type) != ELF_SHT_NULL && yr_le32toh(section->type) != ELF_SHT_NOBITS && rva >= yr_le64toh(section->addr) && rva < yr_le64toh(section->addr) + yr_le64toh(section->size)) { return yr_le64toh(section->offset) + (rva - yr_le64toh(section->addr)); } section++; } } return 0; } uint64_t yr_get_entry_point_offset(const uint8_t* buffer, size_t buffer_length) { PIMAGE_NT_HEADERS32 pe_header; elf32_header_t* elf_header32; elf64_header_t* elf_header64; pe_header = yr_get_pe_header(buffer, buffer_length); if (pe_header != NULL) { return yr_pe_rva_to_offset( pe_header, yr_le32toh(pe_header->OptionalHeader.AddressOfEntryPoint), buffer_length - ((uint8_t*) pe_header - buffer)); } switch (yr_get_elf_type(buffer, buffer_length)) { case ELF_CLASS_32: elf_header32 = (elf32_header_t*) buffer; return yr_elf_rva_to_offset_32( elf_header32, yr_le32toh(elf_header32->entry), buffer_length); case ELF_CLASS_64: elf_header64 = (elf64_header_t*) buffer; return yr_elf_rva_to_offset_64( elf_header64, yr_le64toh(elf_header64->entry), buffer_length); } return YR_UNDEFINED; } uint64_t yr_get_entry_point_address( const uint8_t* buffer, size_t buffer_length, uint64_t base_address) { PIMAGE_NT_HEADERS32 pe_header; elf32_header_t* elf_header32; elf64_header_t* elf_header64; pe_header = yr_get_pe_header(buffer, buffer_length); // If file is PE but not a DLL. if (pe_header != NULL && !(pe_header->FileHeader.Characteristics & IMAGE_FILE_DLL)) return base_address + pe_header->OptionalHeader.AddressOfEntryPoint; // If file is executable ELF, not shared library. switch (yr_get_elf_type(buffer, buffer_length)) { case ELF_CLASS_32: elf_header32 = (elf32_header_t*) buffer; if (elf_header32->type == ELF_ET_EXEC) return elf_header32->entry; break; case ELF_CLASS_64: elf_header64 = (elf64_header_t*) buffer; if (elf_header64->type == ELF_ET_EXEC) return elf_header64->entry; break; } return YR_UNDEFINED; } yara-4.1.3/libyara/filemap.c000066400000000000000000000232161413423160300156600ustar00rootroot00000000000000/* Copyright (c) 2007-2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(_WIN32) || defined(__CYGWIN__) #include #else #include #include #include #endif #include #include //////////////////////////////////////////////////////////////////////////////// // Maps a whole file into memory. // // Args: // file_path: Path of the file to map. // pmapped_file: Pointer to a YR_MAPPED_FILE that will be filled with // information about the mapping. // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_COULD_NOT_OPEN_FILE // ERROR_COULD_NOT_MAP_FILE // YR_API int yr_filemap_map(const char* file_path, YR_MAPPED_FILE* pmapped_file) { return yr_filemap_map_ex(file_path, 0, 0, pmapped_file); } //////////////////////////////////////////////////////////////////////////////// // Maps a portion of a file (specified by descriptor) into memory. // // Args: // file: File descriptor representing the file to map. // offset: File offset where the mapping will begin. This offset must be // multiple of 1MB and not greater than the actual file size. // size: Number of bytes that will be mapped. If zero or greater than the // actual file size all content until the end of the file will be // mapped. // pmapped_file: Pointer to a YR_MAPPED_FILE struct that will be filled with // the new mapping. // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_COULD_NOT_OPEN_FILE // ERROR_COULD_NOT_MAP_FILE // #if defined(_WIN32) || defined(__CYGWIN__) YR_API int yr_filemap_map_fd( YR_FILE_DESCRIPTOR file, uint64_t offset, size_t size, YR_MAPPED_FILE* pmapped_file) { LARGE_INTEGER fs; size_t file_size; pmapped_file->file = file; pmapped_file->mapping = NULL; pmapped_file->data = NULL; pmapped_file->size = 0; // Ensure that offset is aligned to 1MB if (offset >> 20 << 20 != offset) return ERROR_INVALID_ARGUMENT; if (GetFileSizeEx(pmapped_file->file, &fs)) { #ifdef _WIN64 file_size = fs.QuadPart; #else file_size = fs.LowPart; #endif } else { pmapped_file->file = INVALID_HANDLE_VALUE; return ERROR_COULD_NOT_OPEN_FILE; } if (offset > (uint64_t) file_size) return ERROR_COULD_NOT_MAP_FILE; if (size == 0) size = (size_t)(file_size - offset); pmapped_file->size = yr_min(size, (size_t)(file_size - offset)); if (pmapped_file->size != 0) { pmapped_file->mapping = CreateFileMapping( pmapped_file->file, NULL, PAGE_READONLY, 0, 0, NULL); if (pmapped_file->mapping == NULL) { pmapped_file->file = INVALID_HANDLE_VALUE; pmapped_file->size = 0; return ERROR_COULD_NOT_MAP_FILE; } pmapped_file->data = (const uint8_t*) MapViewOfFile( pmapped_file->mapping, FILE_MAP_READ, offset >> 32, offset & 0xFFFFFFFF, pmapped_file->size); if (pmapped_file->data == NULL) { CloseHandle(pmapped_file->mapping); pmapped_file->mapping = NULL; pmapped_file->file = INVALID_HANDLE_VALUE; pmapped_file->size = 0; return ERROR_COULD_NOT_MAP_FILE; } } else { pmapped_file->mapping = NULL; pmapped_file->data = NULL; } return ERROR_SUCCESS; } #else // POSIX #define MAP_EXTRA_FLAGS 0 #if defined(__APPLE__) // MacOS defines some extra flags for mmap.The MAP_RESILIENT_CODESIGN allows // to read from binaries whose code signature is invalid, without this flags // any attempt to read from such binaries causes a crash, see: // https://github.com/VirusTotal/yara/issues/1309. // // Also, reading from files in removable media that becomes unavailable // crashes the program if the MAP_RESILIENT_MEDIA flag is not set. #if defined(MAP_RESILIENT_CODESIGN) #undef MAP_EXTRA_FLAGS #if defined(MAP_RESILIENT_MEDIA) #define MAP_EXTRA_FLAGS MAP_RESILIENT_CODESIGN | MAP_RESILIENT_MEDIA #else #define MAP_EXTRA_FLAGS MAP_RESILIENT_CODESIGN #endif #endif // #if defined(MAP_RESILIENT_CODESIGN) #endif // #if defined (__APPLE__) YR_API int yr_filemap_map_fd( YR_FILE_DESCRIPTOR file, uint64_t offset, size_t size, YR_MAPPED_FILE* pmapped_file) { struct stat st; pmapped_file->file = file; pmapped_file->data = NULL; pmapped_file->size = 0; // Ensure that offset is aligned to 1MB if (offset >> 20 << 20 != offset) return ERROR_INVALID_ARGUMENT; if (fstat(file, &st) != 0 || S_ISDIR(st.st_mode)) return ERROR_COULD_NOT_OPEN_FILE; if (offset > st.st_size) return ERROR_COULD_NOT_MAP_FILE; if (size == 0) size = (size_t)(st.st_size - offset); pmapped_file->size = yr_min(size, (size_t)(st.st_size - offset)); if (pmapped_file->size != 0) { pmapped_file->data = (const uint8_t*) mmap( 0, pmapped_file->size, PROT_READ, MAP_PRIVATE | MAP_EXTRA_FLAGS, pmapped_file->file, offset); if (pmapped_file->data == MAP_FAILED) { pmapped_file->data = NULL; pmapped_file->size = 0; pmapped_file->file = -1; return ERROR_COULD_NOT_MAP_FILE; } madvise((void*) pmapped_file->data, pmapped_file->size, MADV_SEQUENTIAL); } else { pmapped_file->data = NULL; } return ERROR_SUCCESS; } #endif //////////////////////////////////////////////////////////////////////////////// // Maps a portion of a file (specified by path) into memory. // // Args: // file_path: Path of the file to map. // offset: File offset where the mapping will begin. This offset must be // multiple of 1MB and not greater than the actual file size. // size: Number of bytes that will be mapped. If zero or greater than the // actual file size all content until the end of the file will be // mapped. // pmapped_file: Pointer to a YR_MAPPED_FILE struct that will be filled with // the new mapping. // Returns: // ERROR_SUCCESS // ERROR_INVALID_ARGUMENT // ERROR_COULD_NOT_OPEN_FILE // ERROR_COULD_NOT_MAP_FILE // #if defined(_WIN32) || defined(__CYGWIN__) YR_API int yr_filemap_map_ex( const char* file_path, uint64_t offset, size_t size, YR_MAPPED_FILE* pmapped_file) { YR_FILE_DESCRIPTOR fd; int result; if (file_path == NULL) return ERROR_INVALID_ARGUMENT; fd = CreateFileA( file_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (fd == INVALID_HANDLE_VALUE) return ERROR_COULD_NOT_OPEN_FILE; result = yr_filemap_map_fd(fd, offset, size, pmapped_file); if (result != ERROR_SUCCESS) CloseHandle(fd); return result; } #else // POSIX YR_API int yr_filemap_map_ex( const char* file_path, uint64_t offset, size_t size, YR_MAPPED_FILE* pmapped_file) { YR_FILE_DESCRIPTOR fd; int result; if (file_path == NULL) return ERROR_INVALID_ARGUMENT; fd = open(file_path, O_RDONLY); if (fd == -1) return ERROR_COULD_NOT_OPEN_FILE; result = yr_filemap_map_fd(fd, offset, size, pmapped_file); if (result != ERROR_SUCCESS) close(fd); return result; } #endif //////////////////////////////////////////////////////////////////////////////// // Unmaps a file mapping. // // Args: // pmapped_file: Pointer to a YR_MAPPED_FILE that struct. // #ifdef WIN32 YR_API void yr_filemap_unmap_fd(YR_MAPPED_FILE* pmapped_file) { if (pmapped_file->data != NULL) UnmapViewOfFile(pmapped_file->data); if (pmapped_file->mapping != NULL) CloseHandle(pmapped_file->mapping); pmapped_file->mapping = NULL; pmapped_file->data = NULL; pmapped_file->size = 0; } YR_API void yr_filemap_unmap(YR_MAPPED_FILE* pmapped_file) { yr_filemap_unmap_fd(pmapped_file); if (pmapped_file->file != INVALID_HANDLE_VALUE) { CloseHandle(pmapped_file->file); pmapped_file->file = INVALID_HANDLE_VALUE; } } #else // POSIX YR_API void yr_filemap_unmap_fd(YR_MAPPED_FILE* pmapped_file) { if (pmapped_file->data != NULL) munmap((void*) pmapped_file->data, pmapped_file->size); pmapped_file->data = NULL; pmapped_file->size = 0; } YR_API void yr_filemap_unmap(YR_MAPPED_FILE* pmapped_file) { yr_filemap_unmap_fd(pmapped_file); if (pmapped_file->file != -1) { close(pmapped_file->file); pmapped_file->file = -1; } } #endif yara-4.1.3/libyara/grammar.c000066400000000000000000004455561413423160300157100ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse yara_yyparse #define yylex yara_yylex #define yyerror yara_yyerror #define yydebug yara_yydebug #define yynerrs yara_yynerrs /* Copy the first part of user declarations. */ #line 30 "grammar.y" /* yacc.c:339 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #define llabs _abs64 #endif #define YYERROR_VERBOSE #define YYMALLOC yr_malloc #define YYFREE yr_free #define FOR_EXPRESSION_ALL 1 #define FOR_EXPRESSION_ANY 2 #define fail_with_error(e) \ { \ compiler->last_error = e; \ yyerror(yyscanner, compiler, NULL); \ YYERROR; \ } #define fail_if_error(e) \ if (e != ERROR_SUCCESS) \ { \ fail_with_error(e); \ } \ #define check_type_with_cleanup(expression, expected_type, op, cleanup) \ if (((expression.type) & (expected_type)) == 0) \ { \ switch(expression.type) \ { \ case EXPRESSION_TYPE_INTEGER: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"integer\" for " op " operator"); \ break; \ case EXPRESSION_TYPE_FLOAT: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"float\" for " op " operator"); \ break; \ case EXPRESSION_TYPE_STRING: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"string\" for " op " operator"); \ break; \ case EXPRESSION_TYPE_BOOLEAN: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"boolean\" for " op " operator"); \ break; \ } \ cleanup; \ compiler->last_error = ERROR_WRONG_TYPE; \ yyerror(yyscanner, compiler, NULL); \ YYERROR; \ } // check_type(expression, EXPRESSION_TYPE_INTEGER | EXPRESSION_TYPE_FLOAT) is // used to ensure that the type of "expression" is either integer or float. #define check_type(expression, expected_type, op) \ check_type_with_cleanup(expression, expected_type, op, ) #define loop_vars_cleanup(loop_index) \ { \ YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[loop_index]; \ for (int i = 0; i < loop_ctx->vars_count; i++) \ { \ yr_free((void*) loop_ctx->vars[i].identifier.ptr); \ loop_ctx->vars[i].identifier.ptr = NULL; \ loop_ctx->vars[i].identifier.ref = YR_ARENA_NULL_REF; \ } \ loop_ctx->vars_count = 0; \ } \ // Given a YR_EXPRESSION returns its identifier. It returns identifier.ptr if // not NULL and relies on identifier.ref if otherwise. #define expression_identifier(expr) \ ((expr).identifier.ptr != NULL ? \ (expr).identifier.ptr : \ (const char*) yr_arena_ref_to_ptr(compiler->arena, &(expr).identifier.ref)) #define DEFAULT_BASE64_ALPHABET \ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" #line 182 "grammar.c" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* In a future release of Bison, this section will be replaced by #include "y.tab.h". */ #ifndef YY_YARA_YY_GRAMMAR_H_INCLUDED # define YY_YARA_YY_GRAMMAR_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yara_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { _END_OF_FILE_ = 0, _END_OF_INCLUDED_FILE_ = 258, _DOT_DOT_ = 259, _RULE_ = 260, _PRIVATE_ = 261, _GLOBAL_ = 262, _META_ = 263, _STRINGS_ = 264, _CONDITION_ = 265, _IDENTIFIER_ = 266, _STRING_IDENTIFIER_ = 267, _STRING_COUNT_ = 268, _STRING_OFFSET_ = 269, _STRING_LENGTH_ = 270, _STRING_IDENTIFIER_WITH_WILDCARD_ = 271, _NUMBER_ = 272, _DOUBLE_ = 273, _INTEGER_FUNCTION_ = 274, _TEXT_STRING_ = 275, _HEX_STRING_ = 276, _REGEXP_ = 277, _ASCII_ = 278, _WIDE_ = 279, _XOR_ = 280, _BASE64_ = 281, _BASE64_WIDE_ = 282, _NOCASE_ = 283, _FULLWORD_ = 284, _AT_ = 285, _FILESIZE_ = 286, _ENTRYPOINT_ = 287, _ALL_ = 288, _ANY_ = 289, _IN_ = 290, _OF_ = 291, _FOR_ = 292, _THEM_ = 293, _MATCHES_ = 294, _CONTAINS_ = 295, _STARTSWITH_ = 296, _ENDSWITH_ = 297, _ICONTAINS_ = 298, _ISTARTSWITH_ = 299, _IENDSWITH_ = 300, _IMPORT_ = 301, _TRUE_ = 302, _FALSE_ = 303, _OR_ = 304, _AND_ = 305, _NOT_ = 306, _EQ_ = 307, _NEQ_ = 308, _LT_ = 309, _LE_ = 310, _GT_ = 311, _GE_ = 312, _SHIFT_LEFT_ = 313, _SHIFT_RIGHT_ = 314, UNARY_MINUS = 315 }; #endif /* Tokens. */ #define _END_OF_FILE_ 0 #define _END_OF_INCLUDED_FILE_ 258 #define _DOT_DOT_ 259 #define _RULE_ 260 #define _PRIVATE_ 261 #define _GLOBAL_ 262 #define _META_ 263 #define _STRINGS_ 264 #define _CONDITION_ 265 #define _IDENTIFIER_ 266 #define _STRING_IDENTIFIER_ 267 #define _STRING_COUNT_ 268 #define _STRING_OFFSET_ 269 #define _STRING_LENGTH_ 270 #define _STRING_IDENTIFIER_WITH_WILDCARD_ 271 #define _NUMBER_ 272 #define _DOUBLE_ 273 #define _INTEGER_FUNCTION_ 274 #define _TEXT_STRING_ 275 #define _HEX_STRING_ 276 #define _REGEXP_ 277 #define _ASCII_ 278 #define _WIDE_ 279 #define _XOR_ 280 #define _BASE64_ 281 #define _BASE64_WIDE_ 282 #define _NOCASE_ 283 #define _FULLWORD_ 284 #define _AT_ 285 #define _FILESIZE_ 286 #define _ENTRYPOINT_ 287 #define _ALL_ 288 #define _ANY_ 289 #define _IN_ 290 #define _OF_ 291 #define _FOR_ 292 #define _THEM_ 293 #define _MATCHES_ 294 #define _CONTAINS_ 295 #define _STARTSWITH_ 296 #define _ENDSWITH_ 297 #define _ICONTAINS_ 298 #define _ISTARTSWITH_ 299 #define _IENDSWITH_ 300 #define _IMPORT_ 301 #define _TRUE_ 302 #define _FALSE_ 303 #define _OR_ 304 #define _AND_ 305 #define _NOT_ 306 #define _EQ_ 307 #define _NEQ_ 308 #define _LT_ 309 #define _LE_ 310 #define _GT_ 311 #define _GE_ 312 #define _SHIFT_LEFT_ 313 #define _SHIFT_RIGHT_ 314 #define UNARY_MINUS 315 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 302 "grammar.y" /* yacc.c:355 */ YR_EXPRESSION expression; SIZED_STRING* sized_string; char* c_string; int64_t integer; double double_; YR_MODIFIER modifier; YR_ARENA_REF tag; YR_ARENA_REF rule; YR_ARENA_REF meta; YR_ARENA_REF string; #line 358 "grammar.c" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif int yara_yyparse (void *yyscanner, YR_COMPILER* compiler); #endif /* !YY_YARA_YY_GRAMMAR_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 374 "grammar.c" /* yacc.c:358 */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif #if !defined _Noreturn \ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) # if defined _MSC_VER && 1200 <= _MSC_VER # define _Noreturn __declspec (noreturn) # else # define _Noreturn YY_ATTRIBUTE ((__noreturn__)) # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 396 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 81 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 48 /* YYNRULES -- Number of rules. */ #define YYNRULES 151 /* YYNSTATES -- Number of states. */ #define YYNSTATES 252 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 316 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 67, 62, 2, 75, 76, 65, 63, 80, 64, 77, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 73, 2, 2, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 78, 66, 79, 61, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 71, 60, 72, 68, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 69, 70 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 319, 319, 321, 322, 323, 324, 325, 326, 334, 347, 352, 346, 379, 382, 398, 401, 416, 421, 422, 427, 428, 434, 437, 453, 462, 504, 505, 510, 527, 541, 555, 569, 587, 588, 594, 593, 610, 609, 630, 629, 654, 660, 720, 721, 722, 723, 724, 725, 731, 752, 783, 788, 805, 810, 830, 831, 845, 846, 847, 848, 849, 853, 854, 868, 872, 967, 1015, 1076, 1123, 1124, 1128, 1163, 1216, 1258, 1281, 1287, 1293, 1305, 1315, 1325, 1335, 1345, 1355, 1365, 1379, 1394, 1405, 1482, 1520, 1422, 1679, 1678, 1768, 1774, 1781, 1780, 1826, 1825, 1869, 1876, 1883, 1890, 1897, 1904, 1911, 1915, 1923, 1943, 1971, 2045, 2073, 2081, 2090, 2114, 2129, 2149, 2148, 2154, 2165, 2166, 2171, 2178, 2189, 2193, 2198, 2207, 2211, 2219, 2231, 2245, 2252, 2259, 2284, 2296, 2308, 2323, 2335, 2350, 2393, 2414, 2449, 2484, 2518, 2543, 2560, 2570, 2580, 2590, 2600, 2620, 2640 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "\"end of file\"", "error", "$undefined", "\"end of included file\"", "\"..\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"identifier\"", "\"string identifier\"", "\"string count\"", "\"string offset\"", "\"string length\"", "\"string identifier with wildcard\"", "\"integer number\"", "\"floating point number\"", "\"integer function\"", "\"text string\"", "\"hex string\"", "\"regular expression\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"\"", "\"==\"", "\"!=\"", "\"<\"", "\"<=\"", "\">\"", "\">=\"", "\"<<\"", "\">>\"", "'|'", "'^'", "'&'", "'+'", "'-'", "'*'", "'\\\\'", "'%'", "'~'", "UNARY_MINUS", "\"include\"", "'{'", "'}'", "':'", "'='", "'('", "')'", "'.'", "'['", "']'", "','", "$accept", "rules", "import", "rule", "@1", "$@2", "meta", "strings", "condition", "rule_modifiers", "rule_modifier", "tags", "tag_list", "meta_declarations", "meta_declaration", "string_declarations", "string_declaration", "$@3", "$@4", "$@5", "string_modifiers", "string_modifier", "regexp_modifiers", "regexp_modifier", "hex_modifiers", "hex_modifier", "identifier", "arguments", "arguments_list", "regexp", "boolean_expression", "expression", "$@6", "$@7", "$@8", "$@9", "$@10", "for_variables", "iterator", "integer_set", "range", "integer_enumeration", "string_set", "$@11", "string_enumeration", "string_enumeration_item", "for_expression", "primary_expression", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 124, 94, 38, 43, 45, 42, 92, 37, 126, 315, 316, 123, 125, 58, 61, 40, 41, 46, 91, 93, 44 }; # endif #define YYPACT_NINF -74 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-74))) #define YYTABLE_NINF -124 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int16 yypact[] = { -74, 153, -74, -34, -74, -4, -74, -74, 114, -74, -74, -74, -74, 7, -74, -74, -74, -74, -36, 16, -8, -74, 73, 62, -74, 30, 107, 98, 50, -74, 56, 98, -74, 124, 122, 14, -74, 70, 124, -74, 94, 117, -74, -74, -74, -74, 152, 59, -74, 54, -74, -74, 176, 190, 177, -74, -6, -74, 119, 135, -74, -74, 151, -74, -74, -74, -74, -74, -74, 93, -74, -74, 54, 120, 120, 54, 68, -74, 100, -74, 207, 213, -74, -74, -74, 120, 169, 120, 120, 120, 120, 81, 329, -74, -74, -74, 100, 170, 175, 54, 234, 120, -74, -74, 39, 225, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 258, 142, 242, 329, 120, -74, 230, 240, 272, 291, -74, 39, 239, -74, -74, 183, 180, 123, -74, 262, 54, 54, -74, -74, -74, -74, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 329, 158, 158, 116, 31, 127, 97, 97, -74, -74, -74, -74, -74, -74, 186, 187, 188, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, 143, -74, -74, -74, 235, -74, -20, -74, 54, -74, 260, -74, -2, 294, 292, 293, 120, -74, 0, 303, 123, -74, -74, -48, -74, -51, 241, 264, 310, 243, 120, 68, 269, -74, -74, -74, -74, -2, 298, -74, -74, -74, -74, 54, 3, 143, -74, -74, 267, -46, -74, 120, 270, -74, -74, 329, 54, -41, -74 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 2, 0, 1, 18, 8, 0, 4, 3, 0, 7, 6, 5, 9, 0, 20, 21, 19, 10, 22, 0, 0, 24, 23, 13, 25, 0, 15, 0, 0, 11, 0, 14, 26, 0, 0, 0, 27, 0, 16, 33, 0, 0, 29, 28, 31, 32, 0, 35, 34, 0, 12, 30, 0, 0, 0, 65, 84, 133, 135, 137, 130, 131, 0, 132, 73, 127, 128, 124, 125, 0, 75, 76, 0, 0, 0, 0, 138, 151, 17, 74, 0, 105, 41, 55, 62, 0, 0, 0, 0, 0, 0, 0, 123, 94, 139, 148, 0, 74, 105, 69, 0, 0, 97, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 38, 40, 85, 0, 86, 0, 0, 0, 0, 87, 0, 0, 106, 126, 0, 70, 71, 66, 0, 0, 0, 118, 116, 93, 77, 78, 80, 82, 79, 81, 83, 103, 104, 99, 101, 100, 102, 149, 150, 147, 145, 146, 140, 141, 142, 143, 144, 47, 44, 43, 48, 51, 53, 45, 46, 42, 61, 58, 57, 59, 60, 56, 64, 63, 0, 134, 136, 129, 0, 107, 0, 68, 0, 67, 98, 96, 0, 0, 0, 0, 0, 91, 0, 0, 72, 121, 122, 0, 119, 0, 0, 0, 0, 0, 0, 109, 0, 110, 112, 108, 117, 0, 0, 49, 52, 54, 113, 0, 0, 114, 89, 120, 0, 0, 111, 0, 0, 50, 92, 115, 0, 0, 90 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -74, -74, 313, 341, -74, -74, -74, -74, -74, -74, -74, -74, -74, -74, 315, -74, 309, -74, -74, -74, -74, -74, -74, -74, -74, -74, 148, -74, -74, 255, -49, -73, -74, -74, -74, -74, -74, -74, -74, -74, 150, -74, 223, -74, -74, 133, 295, -68 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 1, 6, 7, 18, 34, 26, 29, 41, 8, 16, 20, 22, 31, 32, 38, 39, 52, 53, 54, 128, 184, 129, 190, 130, 192, 76, 143, 144, 77, 96, 79, 140, 245, 222, 149, 148, 199, 225, 226, 133, 237, 152, 205, 216, 217, 80, 81 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int16 yytable[] = { 78, 92, 97, 102, 103, 94, 95, 98, 102, 103, 214, 55, 5, 231, 215, 211, 12, 131, 17, 134, 135, 136, 137, 93, 85, 232, 145, 21, 229, 86, 247, 42, 230, 147, 43, 251, 9, 19, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 212, 44, 45, 23, 193, 55, 56, 57, 58, 59, 25, 60, 61, 62, 63, 223, 64, 150, 46, 243, -39, -37, 138, 244, 24, 65, 66, 67, 68, 118, 119, 69, -88, 122, 123, 124, 125, 126, 127, 203, 204, 70, 71, 27, 55, 72, 57, 58, 59, 30, 60, 61, 62, 63, 151, 64, 28, 139, 73, 13, 14, 15, 74, 33, 65, 66, 67, 68, 213, 75, 35, 55, 40, 57, 58, 59, 37, 60, 61, 62, 63, 221, 64, 99, 47, 100, 101, 209, 185, 102, 103, 65, 66, 2, 3, 238, 4, 73, -18, -18, -18, 74, 125, 126, 127, 186, 187, 49, 90, 51, 188, 189, -74, -74, 118, 119, 248, 121, 122, 123, 124, 125, 126, 127, 73, 118, 119, 242, 74, 50, 123, 124, 125, 126, 127, 90, 82, 87, 84, 5, 250, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, -123, 83, 88, 105, 106, 107, 108, 109, 110, 111, 123, 124, 125, 126, 127, 89, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 104, 132, 146, 141, 64, 191, -123, 198, 142, 105, 106, 107, 108, 109, 110, 111, 200, 201, 206, 207, 208, 176, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 177, 178, 179, 180, 181, 182, 183, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 210, 194, 103, 218, 219, 220, 228, 241, 10, 233, 236, 195, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 234, 202, 239, 246, 11, 249, 36, 48, 196, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 224, 153, 227, 197, 240, 91, 0, 0, 142, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 235, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127 }; static const yytype_int16 yycheck[] = { 49, 69, 75, 49, 50, 73, 74, 75, 49, 50, 12, 11, 46, 64, 16, 35, 20, 85, 11, 87, 88, 89, 90, 72, 30, 76, 99, 11, 76, 35, 76, 17, 80, 101, 20, 76, 70, 73, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 80, 47, 48, 71, 132, 11, 12, 13, 14, 15, 8, 17, 18, 19, 20, 75, 22, 38, 64, 76, 21, 22, 1, 80, 11, 31, 32, 33, 34, 58, 59, 37, 11, 62, 63, 64, 65, 66, 67, 148, 149, 47, 48, 73, 11, 51, 13, 14, 15, 11, 17, 18, 19, 20, 75, 22, 9, 36, 64, 5, 6, 7, 68, 73, 31, 32, 33, 34, 201, 75, 74, 11, 10, 13, 14, 15, 12, 17, 18, 19, 20, 209, 22, 75, 74, 77, 78, 4, 6, 49, 50, 31, 32, 0, 1, 223, 3, 64, 5, 6, 7, 68, 65, 66, 67, 23, 24, 73, 75, 17, 28, 29, 49, 50, 58, 59, 244, 61, 62, 63, 64, 65, 66, 67, 64, 58, 59, 236, 68, 72, 63, 64, 65, 66, 67, 75, 20, 78, 21, 46, 249, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 36, 22, 78, 39, 40, 41, 42, 43, 44, 45, 63, 64, 65, 66, 67, 75, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 36, 75, 11, 76, 22, 6, 36, 11, 76, 39, 40, 41, 42, 43, 44, 45, 76, 80, 75, 75, 75, 6, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 23, 24, 25, 26, 27, 28, 29, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 73, 79, 50, 17, 20, 20, 11, 17, 3, 76, 75, 79, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 76, 79, 73, 76, 3, 75, 31, 38, 76, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 211, 105, 211, 139, 230, 69, -1, -1, 76, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, -1, -1, -1, -1, -1, -1, -1, -1, 76, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 82, 0, 1, 3, 46, 83, 84, 90, 70, 83, 84, 20, 5, 6, 7, 91, 11, 85, 73, 92, 11, 93, 71, 11, 8, 87, 73, 9, 88, 11, 94, 95, 73, 86, 74, 95, 12, 96, 97, 10, 89, 17, 20, 47, 48, 64, 74, 97, 73, 72, 17, 98, 99, 100, 11, 12, 13, 14, 15, 17, 18, 19, 20, 22, 31, 32, 33, 34, 37, 47, 48, 51, 64, 68, 75, 107, 110, 111, 112, 127, 128, 20, 22, 21, 30, 35, 78, 78, 75, 75, 127, 128, 111, 128, 128, 111, 112, 128, 75, 77, 78, 49, 50, 36, 39, 40, 41, 42, 43, 44, 45, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 101, 103, 105, 128, 75, 121, 128, 128, 128, 128, 1, 36, 113, 76, 76, 108, 109, 112, 11, 128, 117, 116, 38, 75, 123, 110, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 6, 23, 24, 25, 26, 27, 28, 29, 102, 6, 23, 24, 28, 29, 104, 6, 106, 128, 79, 79, 76, 123, 11, 118, 76, 80, 79, 111, 111, 124, 75, 75, 75, 4, 73, 35, 80, 112, 12, 16, 125, 126, 17, 20, 20, 128, 115, 75, 107, 119, 120, 121, 11, 76, 80, 64, 76, 76, 76, 76, 75, 122, 128, 73, 126, 17, 111, 76, 80, 114, 76, 76, 128, 75, 111, 76 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 81, 82, 82, 82, 82, 82, 82, 82, 83, 85, 86, 84, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 95, 95, 95, 96, 96, 98, 97, 99, 97, 100, 97, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 104, 104, 104, 104, 104, 105, 105, 106, 107, 107, 107, 107, 108, 108, 109, 109, 110, 111, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 113, 114, 112, 115, 112, 112, 112, 116, 112, 117, 112, 112, 112, 112, 112, 112, 112, 112, 112, 118, 118, 119, 119, 120, 120, 121, 122, 122, 124, 123, 123, 125, 125, 126, 126, 127, 127, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 0, 2, 2, 3, 3, 3, 2, 2, 0, 0, 11, 0, 3, 0, 3, 3, 0, 2, 1, 1, 0, 2, 1, 2, 1, 2, 3, 3, 4, 3, 3, 1, 2, 0, 5, 0, 5, 0, 5, 0, 2, 1, 1, 1, 1, 1, 1, 4, 6, 1, 4, 1, 4, 0, 2, 1, 1, 1, 1, 1, 0, 2, 1, 1, 3, 4, 4, 0, 1, 1, 3, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 0, 0, 11, 0, 9, 3, 2, 0, 4, 0, 4, 3, 3, 3, 3, 3, 3, 1, 3, 1, 3, 1, 1, 3, 1, 5, 1, 3, 0, 4, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 4, 1, 1, 1, 1, 4, 1, 4, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (yyscanner, compiler, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value, yyscanner, compiler); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, void *yyscanner, YR_COMPILER* compiler) { FILE *yyo = yyoutput; YYUSE (yyo); YYUSE (yyscanner); YYUSE (compiler); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, void *yyscanner, YR_COMPILER* compiler) { YYFPRINTF (yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep, yyscanner, compiler); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, void *yyscanner, YR_COMPILER* compiler) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]) , yyscanner, compiler); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, Rule, yyscanner, compiler); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, void *yyscanner, YR_COMPILER* compiler) { YYUSE (yyvaluep); YYUSE (yyscanner); YYUSE (compiler); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { case 11: /* "identifier" */ #line 272 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1437 "grammar.c" /* yacc.c:1258 */ break; case 12: /* "string identifier" */ #line 276 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1443 "grammar.c" /* yacc.c:1258 */ break; case 13: /* "string count" */ #line 273 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1449 "grammar.c" /* yacc.c:1258 */ break; case 14: /* "string offset" */ #line 274 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1455 "grammar.c" /* yacc.c:1258 */ break; case 15: /* "string length" */ #line 275 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1461 "grammar.c" /* yacc.c:1258 */ break; case 16: /* "string identifier with wildcard" */ #line 277 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1467 "grammar.c" /* yacc.c:1258 */ break; case 20: /* "text string" */ #line 278 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).sized_string)); ((*yyvaluep).sized_string) = NULL; } #line 1473 "grammar.c" /* yacc.c:1258 */ break; case 21: /* "hex string" */ #line 279 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).sized_string)); ((*yyvaluep).sized_string) = NULL; } #line 1479 "grammar.c" /* yacc.c:1258 */ break; case 22: /* "regular expression" */ #line 280 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).sized_string)); ((*yyvaluep).sized_string) = NULL; } #line 1485 "grammar.c" /* yacc.c:1258 */ break; case 101: /* string_modifiers */ #line 293 "grammar.y" /* yacc.c:1258 */ { if (((*yyvaluep).modifier).alphabet != NULL) { yr_free(((*yyvaluep).modifier).alphabet); ((*yyvaluep).modifier).alphabet = NULL; } } #line 1497 "grammar.c" /* yacc.c:1258 */ break; case 102: /* string_modifier */ #line 285 "grammar.y" /* yacc.c:1258 */ { if (((*yyvaluep).modifier).alphabet != NULL) { yr_free(((*yyvaluep).modifier).alphabet); ((*yyvaluep).modifier).alphabet = NULL; } } #line 1509 "grammar.c" /* yacc.c:1258 */ break; case 108: /* arguments */ #line 282 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1515 "grammar.c" /* yacc.c:1258 */ break; case 109: /* arguments_list */ #line 283 "grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).c_string)); ((*yyvaluep).c_string) = NULL; } #line 1521 "grammar.c" /* yacc.c:1258 */ break; default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse (void *yyscanner, YR_COMPILER* compiler) { /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ YY_INITIAL_VALUE (static YYSTYPE yyval_default;) YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); /* Number of syntax errors so far. */ int yynerrs; int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (&yylval, yyscanner, compiler); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 8: #line 327 "grammar.y" /* yacc.c:1663 */ { _yr_compiler_pop_file_name(compiler); } #line 1791 "grammar.c" /* yacc.c:1663 */ break; case 9: #line 335 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_import(yyscanner, (yyvsp[0].sized_string)); yr_free((yyvsp[0].sized_string)); fail_if_error(result); } #line 1803 "grammar.c" /* yacc.c:1663 */ break; case 10: #line 347 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_rule_declaration_phase_1( yyscanner, (int32_t) (yyvsp[-2].integer), (yyvsp[0].c_string), &(yyval.rule))); } #line 1812 "grammar.c" /* yacc.c:1663 */ break; case 11: #line 352 "grammar.y" /* yacc.c:1663 */ { YR_RULE* rule = (YR_RULE*) yr_arena_ref_to_ptr( compiler->arena, &(yyvsp[-4].rule)); rule->tags = (char*) yr_arena_ref_to_ptr( compiler->arena, &(yyvsp[-3].tag)); rule->metas = (YR_META*) yr_arena_ref_to_ptr( compiler->arena, &(yyvsp[-1].meta)); rule->strings = (YR_STRING*) yr_arena_ref_to_ptr( compiler->arena, &(yyvsp[0].string)); } #line 1830 "grammar.c" /* yacc.c:1663 */ break; case 12: #line 366 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_rule_declaration_phase_2( yyscanner, &(yyvsp[-7].rule)); // rule created in phase 1 yr_free((yyvsp[-8].c_string)); fail_if_error(result); } #line 1843 "grammar.c" /* yacc.c:1663 */ break; case 13: #line 379 "grammar.y" /* yacc.c:1663 */ { (yyval.meta) = YR_ARENA_NULL_REF; } #line 1851 "grammar.c" /* yacc.c:1663 */ break; case 14: #line 383 "grammar.y" /* yacc.c:1663 */ { YR_META* meta = yr_arena_get_ptr( compiler->arena, YR_METAS_TABLE, (compiler->current_meta_idx - 1) * sizeof(YR_META)); meta->flags |= META_FLAGS_LAST_IN_RULE; (yyval.meta) = (yyvsp[0].meta); } #line 1866 "grammar.c" /* yacc.c:1663 */ break; case 15: #line 398 "grammar.y" /* yacc.c:1663 */ { (yyval.string) = YR_ARENA_NULL_REF; } #line 1874 "grammar.c" /* yacc.c:1663 */ break; case 16: #line 402 "grammar.y" /* yacc.c:1663 */ { YR_STRING* string = (YR_STRING*) yr_arena_get_ptr( compiler->arena, YR_STRINGS_TABLE, (compiler->current_string_idx - 1) * sizeof(YR_STRING)); string->flags |= STRING_FLAGS_LAST_IN_RULE; (yyval.string) = (yyvsp[0].string); } #line 1889 "grammar.c" /* yacc.c:1663 */ break; case 18: #line 421 "grammar.y" /* yacc.c:1663 */ { (yyval.integer) = 0; } #line 1895 "grammar.c" /* yacc.c:1663 */ break; case 19: #line 422 "grammar.y" /* yacc.c:1663 */ { (yyval.integer) = (yyvsp[-1].integer) | (yyvsp[0].integer); } #line 1901 "grammar.c" /* yacc.c:1663 */ break; case 20: #line 427 "grammar.y" /* yacc.c:1663 */ { (yyval.integer) = RULE_FLAGS_PRIVATE; } #line 1907 "grammar.c" /* yacc.c:1663 */ break; case 21: #line 428 "grammar.y" /* yacc.c:1663 */ { (yyval.integer) = RULE_FLAGS_GLOBAL; } #line 1913 "grammar.c" /* yacc.c:1663 */ break; case 22: #line 434 "grammar.y" /* yacc.c:1663 */ { (yyval.tag) = YR_ARENA_NULL_REF; } #line 1921 "grammar.c" /* yacc.c:1663 */ break; case 23: #line 438 "grammar.y" /* yacc.c:1663 */ { // Tags list is represented in the arena as a sequence // of null-terminated strings, the sequence ends with an // additional null character. Here we write the ending null //character. Example: tag1\0tag2\0tag3\0\0 fail_if_error(yr_arena_write_string( yyget_extra(yyscanner)->arena, YR_SZ_POOL, "", NULL)); (yyval.tag) = (yyvsp[0].tag); } #line 1937 "grammar.c" /* yacc.c:1663 */ break; case 24: #line 454 "grammar.y" /* yacc.c:1663 */ { int result = yr_arena_write_string( yyget_extra(yyscanner)->arena, YR_SZ_POOL, (yyvsp[0].c_string), &(yyval.tag)); yr_free((yyvsp[0].c_string)); fail_if_error(result); } #line 1950 "grammar.c" /* yacc.c:1663 */ break; case 25: #line 463 "grammar.y" /* yacc.c:1663 */ { YR_ARENA_REF ref; // Write the new tag identifier. int result = yr_arena_write_string( yyget_extra(yyscanner)->arena, YR_SZ_POOL, (yyvsp[0].c_string), &ref); yr_free((yyvsp[0].c_string)); fail_if_error(result); // Get the address for the tag identifier just written. char* new_tag = (char*) yr_arena_ref_to_ptr( compiler->arena, &ref); // Take the address of first tag's identifier in the list. char* tag = (char*) yr_arena_ref_to_ptr( compiler->arena, &(yyval.tag)); // Search for duplicated tags. Tags are written one after // the other, with zeroes in between (i.e: tag1/0tag2/0tag3) // that's why can use tag < new_tag as the condition for the // loop. while (tag < new_tag) { if (strcmp(tag, new_tag) == 0) { yr_compiler_set_error_extra_info(compiler, tag); fail_with_error(ERROR_DUPLICATED_TAG_IDENTIFIER); } tag += strlen(tag) + 1; } (yyval.tag) = (yyvsp[-1].tag); } #line 1991 "grammar.c" /* yacc.c:1663 */ break; case 26: #line 504 "grammar.y" /* yacc.c:1663 */ { (yyval.meta) = (yyvsp[0].meta); } #line 1997 "grammar.c" /* yacc.c:1663 */ break; case 27: #line 505 "grammar.y" /* yacc.c:1663 */ { (yyval.meta) = (yyvsp[-1].meta); } #line 2003 "grammar.c" /* yacc.c:1663 */ break; case 28: #line 511 "grammar.y" /* yacc.c:1663 */ { SIZED_STRING* sized_string = (yyvsp[0].sized_string); int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_STRING, (yyvsp[-2].c_string), sized_string->c_string, 0, &(yyval.meta)); yr_free((yyvsp[-2].c_string)); yr_free((yyvsp[0].sized_string)); fail_if_error(result); } #line 2024 "grammar.c" /* yacc.c:1663 */ break; case 29: #line 528 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_INTEGER, (yyvsp[-2].c_string), NULL, (yyvsp[0].integer), &(yyval.meta)); yr_free((yyvsp[-2].c_string)); fail_if_error(result); } #line 2042 "grammar.c" /* yacc.c:1663 */ break; case 30: #line 542 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_INTEGER, (yyvsp[-3].c_string), NULL, -(yyvsp[0].integer), &(yyval.meta)); yr_free((yyvsp[-3].c_string)); fail_if_error(result); } #line 2060 "grammar.c" /* yacc.c:1663 */ break; case 31: #line 556 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_BOOLEAN, (yyvsp[-2].c_string), NULL, true, &(yyval.meta)); yr_free((yyvsp[-2].c_string)); fail_if_error(result); } #line 2078 "grammar.c" /* yacc.c:1663 */ break; case 32: #line 570 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_BOOLEAN, (yyvsp[-2].c_string), NULL, false, &(yyval.meta)); yr_free((yyvsp[-2].c_string)); fail_if_error(result); } #line 2096 "grammar.c" /* yacc.c:1663 */ break; case 33: #line 587 "grammar.y" /* yacc.c:1663 */ { (yyval.string) = (yyvsp[0].string); } #line 2102 "grammar.c" /* yacc.c:1663 */ break; case 34: #line 588 "grammar.y" /* yacc.c:1663 */ { (yyval.string) = (yyvsp[-1].string); } #line 2108 "grammar.c" /* yacc.c:1663 */ break; case 35: #line 594 "grammar.y" /* yacc.c:1663 */ { compiler->current_line = yyget_lineno(yyscanner); } #line 2116 "grammar.c" /* yacc.c:1663 */ break; case 36: #line 598 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_string_declaration( yyscanner, (yyvsp[0].modifier), (yyvsp[-4].c_string), (yyvsp[-1].sized_string), &(yyval.string)); yr_free((yyvsp[-4].c_string)); yr_free((yyvsp[-1].sized_string)); yr_free((yyvsp[0].modifier).alphabet); fail_if_error(result); compiler->current_line = 0; } #line 2132 "grammar.c" /* yacc.c:1663 */ break; case 37: #line 610 "grammar.y" /* yacc.c:1663 */ { compiler->current_line = yyget_lineno(yyscanner); } #line 2140 "grammar.c" /* yacc.c:1663 */ break; case 38: #line 614 "grammar.y" /* yacc.c:1663 */ { int result; (yyvsp[0].modifier).flags |= STRING_FLAGS_REGEXP; result = yr_parser_reduce_string_declaration( yyscanner, (yyvsp[0].modifier), (yyvsp[-4].c_string), (yyvsp[-1].sized_string), &(yyval.string)); yr_free((yyvsp[-4].c_string)); yr_free((yyvsp[-1].sized_string)); fail_if_error(result); compiler->current_line = 0; } #line 2160 "grammar.c" /* yacc.c:1663 */ break; case 39: #line 630 "grammar.y" /* yacc.c:1663 */ { compiler->current_line = yyget_lineno(yyscanner); } #line 2168 "grammar.c" /* yacc.c:1663 */ break; case 40: #line 634 "grammar.y" /* yacc.c:1663 */ { int result; (yyvsp[0].modifier).flags |= STRING_FLAGS_HEXADECIMAL; result = yr_parser_reduce_string_declaration( yyscanner, (yyvsp[0].modifier), (yyvsp[-4].c_string), (yyvsp[-1].sized_string), &(yyval.string)); yr_free((yyvsp[-4].c_string)); yr_free((yyvsp[-1].sized_string)); fail_if_error(result); compiler->current_line = 0; } #line 2188 "grammar.c" /* yacc.c:1663 */ break; case 41: #line 654 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = 0; (yyval.modifier).xor_min = 0; (yyval.modifier).xor_max = 0; (yyval.modifier).alphabet = NULL; } #line 2199 "grammar.c" /* yacc.c:1663 */ break; case 42: #line 661 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier) = (yyvsp[-1].modifier); // Only set the xor minimum and maximum if we are dealing with the // xor modifier. If we don't check for this then we can end up with // "xor wide" resulting in whatever is on the stack for "wide" // overwriting the values for xor. if ((yyvsp[0].modifier).flags & STRING_FLAGS_XOR) { (yyval.modifier).xor_min = (yyvsp[0].modifier).xor_min; (yyval.modifier).xor_max = (yyvsp[0].modifier).xor_max; } // Only set the base64 alphabet if we are dealing with the base64 // modifier. If we don't check for this then we can end up with // "base64 ascii" resulting in whatever is on the stack for "ascii" // overwriting the values for base64. if (((yyvsp[0].modifier).flags & STRING_FLAGS_BASE64) || ((yyvsp[0].modifier).flags & STRING_FLAGS_BASE64_WIDE)) { if ((yyval.modifier).alphabet != NULL) { if (ss_compare((yyval.modifier).alphabet, (yyvsp[0].modifier).alphabet) != 0) { yr_compiler_set_error_extra_info( compiler, "can not specify multiple alphabets"); yr_free((yyvsp[0].modifier).alphabet); yr_free((yyval.modifier).alphabet); fail_with_error(ERROR_INVALID_MODIFIER); } else { yr_free((yyvsp[0].modifier).alphabet); } } else { (yyval.modifier).alphabet = (yyvsp[0].modifier).alphabet; } } if ((yyval.modifier).flags & (yyvsp[0].modifier).flags) { if ((yyval.modifier).alphabet != NULL) yr_free((yyval.modifier).alphabet); fail_with_error(ERROR_DUPLICATED_MODIFIER); } else { (yyval.modifier).flags = (yyval.modifier).flags | (yyvsp[0].modifier).flags; } } #line 2259 "grammar.c" /* yacc.c:1663 */ break; case 43: #line 720 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_WIDE; } #line 2265 "grammar.c" /* yacc.c:1663 */ break; case 44: #line 721 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_ASCII; } #line 2271 "grammar.c" /* yacc.c:1663 */ break; case 45: #line 722 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_NO_CASE; } #line 2277 "grammar.c" /* yacc.c:1663 */ break; case 46: #line 723 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_FULL_WORD; } #line 2283 "grammar.c" /* yacc.c:1663 */ break; case 47: #line 724 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_PRIVATE; } #line 2289 "grammar.c" /* yacc.c:1663 */ break; case 48: #line 726 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_XOR; (yyval.modifier).xor_min = 0; (yyval.modifier).xor_max = 255; } #line 2299 "grammar.c" /* yacc.c:1663 */ break; case 49: #line 732 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[-1].integer) < 0 || (yyvsp[-1].integer) > 255) { yr_compiler_set_error_extra_info(compiler, "invalid xor range"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); (yyval.modifier).flags = STRING_FLAGS_XOR; (yyval.modifier).xor_min = (uint8_t) (yyvsp[-1].integer); (yyval.modifier).xor_max = (uint8_t) (yyvsp[-1].integer); } #line 2319 "grammar.c" /* yacc.c:1663 */ break; case 50: #line 753 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[-3].integer) < 0) { yr_compiler_set_error_extra_info( compiler, "lower bound for xor range exceeded (min: 0)"); result = ERROR_INVALID_MODIFIER; } if ((yyvsp[-1].integer) > 255) { yr_compiler_set_error_extra_info( compiler, "upper bound for xor range exceeded (max: 255)"); result = ERROR_INVALID_MODIFIER; } if ((yyvsp[-3].integer) > (yyvsp[-1].integer)) { yr_compiler_set_error_extra_info( compiler, "xor lower bound exceeds upper bound"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); (yyval.modifier).flags = STRING_FLAGS_XOR; (yyval.modifier).xor_min = (uint8_t) (yyvsp[-3].integer); (yyval.modifier).xor_max = (uint8_t) (yyvsp[-1].integer); } #line 2354 "grammar.c" /* yacc.c:1663 */ break; case 51: #line 784 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_BASE64; (yyval.modifier).alphabet = ss_new(DEFAULT_BASE64_ALPHABET); } #line 2363 "grammar.c" /* yacc.c:1663 */ break; case 52: #line 789 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[-1].sized_string)->length != 64) { yr_free((yyvsp[-1].sized_string)); yr_compiler_set_error_extra_info( compiler, "length of base64 alphabet must be 64"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); (yyval.modifier).flags = STRING_FLAGS_BASE64; (yyval.modifier).alphabet = (yyvsp[-1].sized_string); } #line 2384 "grammar.c" /* yacc.c:1663 */ break; case 53: #line 806 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_BASE64_WIDE; (yyval.modifier).alphabet = ss_new(DEFAULT_BASE64_ALPHABET); } #line 2393 "grammar.c" /* yacc.c:1663 */ break; case 54: #line 811 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[-1].sized_string)->length != 64) { yr_free((yyvsp[-1].sized_string)); yr_compiler_set_error_extra_info( compiler, "length of base64 alphabet must be 64"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); (yyval.modifier).flags = STRING_FLAGS_BASE64_WIDE; (yyval.modifier).alphabet = (yyvsp[-1].sized_string); } #line 2414 "grammar.c" /* yacc.c:1663 */ break; case 55: #line 830 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = 0; } #line 2420 "grammar.c" /* yacc.c:1663 */ break; case 56: #line 832 "grammar.y" /* yacc.c:1663 */ { if ((yyvsp[-1].modifier).flags & (yyvsp[0].modifier).flags) { fail_with_error(ERROR_DUPLICATED_MODIFIER); } else { (yyval.modifier).flags = (yyvsp[-1].modifier).flags | (yyvsp[0].modifier).flags; } } #line 2435 "grammar.c" /* yacc.c:1663 */ break; case 57: #line 845 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_WIDE; } #line 2441 "grammar.c" /* yacc.c:1663 */ break; case 58: #line 846 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_ASCII; } #line 2447 "grammar.c" /* yacc.c:1663 */ break; case 59: #line 847 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_NO_CASE; } #line 2453 "grammar.c" /* yacc.c:1663 */ break; case 60: #line 848 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_FULL_WORD; } #line 2459 "grammar.c" /* yacc.c:1663 */ break; case 61: #line 849 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_PRIVATE; } #line 2465 "grammar.c" /* yacc.c:1663 */ break; case 62: #line 853 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = 0; } #line 2471 "grammar.c" /* yacc.c:1663 */ break; case 63: #line 855 "grammar.y" /* yacc.c:1663 */ { if ((yyvsp[-1].modifier).flags & (yyvsp[0].modifier).flags) { fail_with_error(ERROR_DUPLICATED_MODIFIER); } else { (yyval.modifier).flags = (yyvsp[-1].modifier).flags | (yyvsp[0].modifier).flags; } } #line 2486 "grammar.c" /* yacc.c:1663 */ break; case 64: #line 868 "grammar.y" /* yacc.c:1663 */ { (yyval.modifier).flags = STRING_FLAGS_PRIVATE; } #line 2492 "grammar.c" /* yacc.c:1663 */ break; case 65: #line 873 "grammar.y" /* yacc.c:1663 */ { YR_EXPRESSION expr; int result = ERROR_SUCCESS; int var_index = yr_parser_lookup_loop_variable(yyscanner, (yyvsp[0].c_string), &expr); if (var_index >= 0) { // The identifier corresponds to a loop variable. result = yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_index, NULL, NULL); // The expression associated to this identifier is the same one // associated to the loop variable. (yyval.expression) = expr; } else { // Search for identifier within the global namespace, where the // externals variables reside. YR_OBJECT* object = (YR_OBJECT*) yr_hash_table_lookup( compiler->objects_table, (yyvsp[0].c_string), NULL); YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE)); if (object == NULL) { // If not found, search within the current namespace. object = (YR_OBJECT*) yr_hash_table_lookup( compiler->objects_table, (yyvsp[0].c_string), ns->name); } if (object != NULL) { YR_ARENA_REF ref; result = _yr_compiler_store_string( compiler, (yyvsp[0].c_string), &ref); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_OBJ_LOAD, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); (yyval.expression).type = EXPRESSION_TYPE_OBJECT; (yyval.expression).value.object = object; (yyval.expression).identifier.ptr = NULL; (yyval.expression).identifier.ref = ref; } else { uint32_t rule_idx = yr_hash_table_lookup_uint32( compiler->rules_table, (yyvsp[0].c_string), ns->name); if (rule_idx != UINT32_MAX) { result = yr_parser_emit_with_arg( yyscanner, OP_PUSH_RULE, rule_idx, NULL, NULL); YR_RULE* rule = _yr_compiler_get_rule_by_idx(compiler, rule_idx); yr_arena_ptr_to_ref(compiler->arena, rule->identifier, &(yyval.expression).identifier.ref); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; (yyval.expression).value.integer = YR_UNDEFINED; (yyval.expression).identifier.ptr = NULL; } else { yr_compiler_set_error_extra_info(compiler, (yyvsp[0].c_string)); result = ERROR_UNDEFINED_IDENTIFIER; } } } yr_free((yyvsp[0].c_string)); fail_if_error(result); } #line 2591 "grammar.c" /* yacc.c:1663 */ break; case 66: #line 968 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; YR_OBJECT* field = NULL; if ((yyvsp[-2].expression).type == EXPRESSION_TYPE_OBJECT && (yyvsp[-2].expression).value.object->type == OBJECT_TYPE_STRUCTURE) { field = yr_object_lookup_field((yyvsp[-2].expression).value.object, (yyvsp[0].c_string)); if (field != NULL) { YR_ARENA_REF ref; result = _yr_compiler_store_string( compiler, (yyvsp[0].c_string), &ref); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_OBJ_FIELD, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); (yyval.expression).type = EXPRESSION_TYPE_OBJECT; (yyval.expression).value.object = field; (yyval.expression).identifier.ref = ref; (yyval.expression).identifier.ptr = NULL; } else { yr_compiler_set_error_extra_info(compiler, (yyvsp[0].c_string)); result = ERROR_INVALID_FIELD_NAME; } } else { yr_compiler_set_error_extra_info( compiler, expression_identifier((yyvsp[-2].expression))); result = ERROR_NOT_A_STRUCTURE; } yr_free((yyvsp[0].c_string)); fail_if_error(result); } #line 2643 "grammar.c" /* yacc.c:1663 */ break; case 67: #line 1016 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; YR_OBJECT_ARRAY* array; YR_OBJECT_DICTIONARY* dict; if ((yyvsp[-3].expression).type == EXPRESSION_TYPE_OBJECT && (yyvsp[-3].expression).value.object->type == OBJECT_TYPE_ARRAY) { if ((yyvsp[-1].expression).type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "array indexes must be of integer type"); result = ERROR_WRONG_TYPE; } fail_if_error(result); result = yr_parser_emit( yyscanner, OP_INDEX_ARRAY, NULL); array = object_as_array((yyvsp[-3].expression).value.object); (yyval.expression).type = EXPRESSION_TYPE_OBJECT; (yyval.expression).value.object = array->prototype_item; (yyval.expression).identifier.ptr = array->identifier; (yyval.expression).identifier.ref = YR_ARENA_NULL_REF; } else if ((yyvsp[-3].expression).type == EXPRESSION_TYPE_OBJECT && (yyvsp[-3].expression).value.object->type == OBJECT_TYPE_DICTIONARY) { if ((yyvsp[-1].expression).type != EXPRESSION_TYPE_STRING) { yr_compiler_set_error_extra_info( compiler, "dictionary keys must be of string type"); result = ERROR_WRONG_TYPE; } fail_if_error(result); result = yr_parser_emit( yyscanner, OP_LOOKUP_DICT, NULL); dict = object_as_dictionary((yyvsp[-3].expression).value.object); (yyval.expression).type = EXPRESSION_TYPE_OBJECT; (yyval.expression).value.object = dict->prototype_item; (yyval.expression).identifier.ptr = dict->identifier; (yyval.expression).identifier.ref = YR_ARENA_NULL_REF; } else { yr_compiler_set_error_extra_info( compiler, expression_identifier((yyvsp[-3].expression))); result = ERROR_NOT_INDEXABLE; } fail_if_error(result); } #line 2707 "grammar.c" /* yacc.c:1663 */ break; case 68: #line 1077 "grammar.y" /* yacc.c:1663 */ { YR_ARENA_REF ref; int result = ERROR_SUCCESS; YR_OBJECT_FUNCTION* function; if ((yyvsp[-3].expression).type == EXPRESSION_TYPE_OBJECT && (yyvsp[-3].expression).value.object->type == OBJECT_TYPE_FUNCTION) { result = yr_parser_check_types( compiler, object_as_function((yyvsp[-3].expression).value.object), (yyvsp[-1].c_string)); if (result == ERROR_SUCCESS) result = _yr_compiler_store_string( compiler, (yyvsp[-1].c_string), &ref); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_CALL, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); function = object_as_function((yyvsp[-3].expression).value.object); (yyval.expression).type = EXPRESSION_TYPE_OBJECT; (yyval.expression).value.object = function->return_obj; (yyval.expression).identifier.ref = ref; (yyval.expression).identifier.ptr = NULL; } else { yr_compiler_set_error_extra_info( compiler, expression_identifier((yyvsp[-3].expression))); result = ERROR_NOT_A_FUNCTION; } yr_free((yyvsp[-1].c_string)); fail_if_error(result); } #line 2754 "grammar.c" /* yacc.c:1663 */ break; case 69: #line 1123 "grammar.y" /* yacc.c:1663 */ { (yyval.c_string) = yr_strdup(""); } #line 2760 "grammar.c" /* yacc.c:1663 */ break; case 70: #line 1124 "grammar.y" /* yacc.c:1663 */ { (yyval.c_string) = (yyvsp[0].c_string); } #line 2766 "grammar.c" /* yacc.c:1663 */ break; case 71: #line 1129 "grammar.y" /* yacc.c:1663 */ { (yyval.c_string) = (char*) yr_malloc(YR_MAX_FUNCTION_ARGS + 1); if ((yyval.c_string) == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); switch((yyvsp[0].expression).type) { case EXPRESSION_TYPE_INTEGER: strlcpy((yyval.c_string), "i", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_FLOAT: strlcpy((yyval.c_string), "f", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_BOOLEAN: strlcpy((yyval.c_string), "b", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_STRING: strlcpy((yyval.c_string), "s", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_REGEXP: strlcpy((yyval.c_string), "r", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_UNKNOWN: yr_free((yyval.c_string)); yr_compiler_set_error_extra_info( compiler, "unknown type for argument 1 in function call"); fail_with_error(ERROR_WRONG_TYPE); break; default: // An unknown expression type is OK iff an error ocurred. assert(compiler->last_error != ERROR_SUCCESS); } } #line 2805 "grammar.c" /* yacc.c:1663 */ break; case 72: #line 1164 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if (strlen((yyvsp[-2].c_string)) == YR_MAX_FUNCTION_ARGS) { result = ERROR_TOO_MANY_ARGUMENTS; } else { switch((yyvsp[0].expression).type) { case EXPRESSION_TYPE_INTEGER: strlcat((yyvsp[-2].c_string), "i", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_FLOAT: strlcat((yyvsp[-2].c_string), "f", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_BOOLEAN: strlcat((yyvsp[-2].c_string), "b", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_STRING: strlcat((yyvsp[-2].c_string), "s", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_REGEXP: strlcat((yyvsp[-2].c_string), "r", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_UNKNOWN: result = ERROR_WRONG_TYPE; yr_compiler_set_error_extra_info_fmt( compiler, "unknown type for argument %zu in function call", // As we add one character per argument, the length of $1 is // the number of arguments parsed so far, and the argument // represented by is length of $1 plus one. strlen((yyvsp[-2].c_string)) + 1); break; default: // An unknown expression type is OK iff an error ocurred. assert(compiler->last_error != ERROR_SUCCESS); } } if (result != ERROR_SUCCESS) yr_free((yyvsp[-2].c_string)); fail_if_error(result); (yyval.c_string) = (yyvsp[-2].c_string); } #line 2858 "grammar.c" /* yacc.c:1663 */ break; case 73: #line 1217 "grammar.y" /* yacc.c:1663 */ { YR_ARENA_REF re_ref; RE_ERROR error; int result = ERROR_SUCCESS; int re_flags = 0; if ((yyvsp[0].sized_string)->flags & SIZED_STRING_FLAGS_NO_CASE) re_flags |= RE_FLAGS_NO_CASE; if ((yyvsp[0].sized_string)->flags & SIZED_STRING_FLAGS_DOT_ALL) re_flags |= RE_FLAGS_DOT_ALL; result = yr_re_compile( (yyvsp[0].sized_string)->c_string, re_flags, compiler->arena, &re_ref, &error); yr_free((yyvsp[0].sized_string)); if (result == ERROR_INVALID_REGULAR_EXPRESSION) yr_compiler_set_error_extra_info(compiler, error.message); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_PUSH, yr_arena_ref_to_ptr(compiler->arena, &re_ref), NULL, NULL); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_REGEXP; } #line 2900 "grammar.c" /* yacc.c:1663 */ break; case 74: #line 1259 "grammar.y" /* yacc.c:1663 */ { if ((yyvsp[0].expression).type == EXPRESSION_TYPE_STRING) { if (!YR_ARENA_IS_NULL_REF((yyvsp[0].expression).value.sized_string_ref)) { SIZED_STRING* sized_string = yr_arena_ref_to_ptr( compiler->arena, &(yyvsp[0].expression).value.sized_string_ref); yywarning(yyscanner, "using literal string \"%s\" in a boolean operation.", sized_string->c_string); } fail_if_error(yr_parser_emit( yyscanner, OP_STR_TO_BOOL, NULL)); } (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 2924 "grammar.c" /* yacc.c:1663 */ break; case 75: #line 1282 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit_push_const(yyscanner, 1)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 2934 "grammar.c" /* yacc.c:1663 */ break; case 76: #line 1288 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit_push_const(yyscanner, 0)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 2944 "grammar.c" /* yacc.c:1663 */ break; case 77: #line 1294 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "matches"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_REGEXP, "matches"); fail_if_error(yr_parser_emit( yyscanner, OP_MATCHES, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 2960 "grammar.c" /* yacc.c:1663 */ break; case 78: #line 1306 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "contains"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_STRING, "contains"); fail_if_error(yr_parser_emit( yyscanner, OP_CONTAINS, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 2974 "grammar.c" /* yacc.c:1663 */ break; case 79: #line 1316 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "icontains"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_STRING, "icontains"); fail_if_error(yr_parser_emit( yyscanner, OP_ICONTAINS, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 2988 "grammar.c" /* yacc.c:1663 */ break; case 80: #line 1326 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "startswith"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_STRING, "startswith"); fail_if_error(yr_parser_emit( yyscanner, OP_STARTSWITH, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3002 "grammar.c" /* yacc.c:1663 */ break; case 81: #line 1336 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "istartswith"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_STRING, "istartswith"); fail_if_error(yr_parser_emit( yyscanner, OP_ISTARTSWITH, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3016 "grammar.c" /* yacc.c:1663 */ break; case 82: #line 1346 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "endswith"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_STRING, "endswith"); fail_if_error(yr_parser_emit( yyscanner, OP_ENDSWITH, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3030 "grammar.c" /* yacc.c:1663 */ break; case 83: #line 1356 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_STRING, "iendswith"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_STRING, "iendswith"); fail_if_error(yr_parser_emit( yyscanner, OP_IENDSWITH, NULL)); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3044 "grammar.c" /* yacc.c:1663 */ break; case 84: #line 1366 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[0].c_string), OP_FOUND, YR_UNDEFINED); yr_free((yyvsp[0].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3062 "grammar.c" /* yacc.c:1663 */ break; case 85: #line 1380 "grammar.y" /* yacc.c:1663 */ { int result; check_type_with_cleanup((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "at", yr_free((yyvsp[-2].c_string))); result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[-2].c_string), OP_FOUND_AT, (yyvsp[0].expression).value.integer); yr_free((yyvsp[-2].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3081 "grammar.c" /* yacc.c:1663 */ break; case 86: #line 1395 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[-2].c_string), OP_FOUND_IN, YR_UNDEFINED); yr_free((yyvsp[-2].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3096 "grammar.c" /* yacc.c:1663 */ break; case 87: #line 1406 "grammar.y" /* yacc.c:1663 */ { // Free all the loop variable identifiers, including the variables for // the current loop (represented by loop_index), and set loop_index to // -1. This is OK even if we have nested loops. If an error occurs while // parsing the inner loop, it will be propagated to the outer loop // anyways, so it's safe to do this cleanup while processing the error // for the inner loop. for (int i = 0; i <= compiler->loop_index; i++) { loop_vars_cleanup(i); } compiler->loop_index = -1; YYERROR; } #line 3117 "grammar.c" /* yacc.c:1663 */ break; case 88: #line 1482 "grammar.y" /* yacc.c:1663 */ { // var_frame is used for accessing local variables used in this loop. // All local variables are accessed using var_frame as a reference, // like var_frame + 0, var_frame + 1, etc. Here we initialize var_frame // with the correct value, which depends on the number of variables // defined by any outer loops. int var_frame; int result = ERROR_SUCCESS; if (compiler->loop_index + 1 == YR_MAX_LOOP_NESTING) result = ERROR_LOOP_NESTING_LIMIT_EXCEEDED; fail_if_error(result); compiler->loop_index++; // This loop uses internal variables besides the ones explicitly // defined by the user. compiler->loop[compiler->loop_index].vars_internal_count = \ YR_INTERNAL_LOOP_VARS; // Initialize the number of variables, this number will be incremented // as variable declaration are processed by for_variables. compiler->loop[compiler->loop_index].vars_count = 0; var_frame = _yr_compiler_get_var_frame(compiler); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_POP_M, var_frame + 2, NULL, NULL)); } #line 3159 "grammar.c" /* yacc.c:1663 */ break; case 89: #line 1520 "grammar.y" /* yacc.c:1663 */ { YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; YR_FIXUP* fixup; YR_ARENA_REF loop_start_ref; YR_ARENA_REF jmp_offset_ref; int var_frame = _yr_compiler_get_var_frame(compiler); fail_if_error(yr_parser_emit( yyscanner, OP_ITER_NEXT, &loop_start_ref)); // For each variable generate an instruction that pops the value from // the stack and store it into one memory slot starting at var_frame + // YR_INTERNAL_LOOP_VARS because the first YR_INTERNAL_LOOP_VARS slots // in the frame are for the internal variables. for (int i = 0; i < loop_ctx->vars_count; i++) { fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_POP_M, var_frame + YR_INTERNAL_LOOP_VARS + i, NULL, NULL)); } fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JTRUE_P, 0, // still don't know the jump offset, use 0 for now. NULL, &jmp_offset_ref)); // We still don't know the jump's target, so we push a fixup entry // in the stack, so that the jump's offset can be set once we know it. fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; loop_ctx->start_ref = loop_start_ref; } #line 3212 "grammar.c" /* yacc.c:1663 */ break; case 90: #line 1569 "grammar.y" /* yacc.c:1663 */ { int32_t jmp_offset; YR_FIXUP* fixup; YR_ARENA_REF pop_ref; YR_ARENA_REF jmp_offset_ref; int var_frame = _yr_compiler_get_var_frame(compiler); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_ADD_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_INCR_M, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 2, NULL, NULL)); jmp_offset = \ compiler->loop[compiler->loop_index].start_ref.offset - yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION); fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JUNDEF_P, jmp_offset, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 2, NULL, NULL)); jmp_offset = \ compiler->loop[compiler->loop_index].start_ref.offset - yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION); fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JL_P, jmp_offset, NULL, NULL)); fail_if_error(yr_parser_emit( yyscanner, OP_POP, &pop_ref)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JZ, 0, // still don't know the jump offset, use 0 for now. NULL, &jmp_offset_ref)); fail_if_error(yr_parser_emit( yyscanner, OP_POP, NULL)); // Pop from the stack the fixup entry containing the reference to // the jump offset that needs to be fixed. fixup = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup->next; // The fixup entry has a reference to the jump offset that need // to be fixed, convert the address into a pointer. int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); // The reference in the fixup entry points to the jump's offset // but the jump instruction is one byte before, that's why we add // one to the offset. jmp_offset = pop_ref.offset - fixup->ref.offset + 1; // Fix the jump's offset. *jmp_offset_addr = jmp_offset; yr_free(fixup); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 2, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_SWAPUNDEF, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit( yyscanner, OP_INT_GE, NULL)); jmp_offset = \ yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION) - jmp_offset_ref.offset + 1; jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &jmp_offset_ref); *jmp_offset_addr = jmp_offset; loop_vars_cleanup(compiler->loop_index); compiler->loop_index--; (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3326 "grammar.c" /* yacc.c:1663 */ break; case 91: #line 1679 "grammar.y" /* yacc.c:1663 */ { YR_ARENA_REF ref; int result = ERROR_SUCCESS; int var_frame; if (compiler->loop_index + 1 == YR_MAX_LOOP_NESTING) result = ERROR_LOOP_NESTING_LIMIT_EXCEEDED; if (compiler->loop_for_of_var_index != -1) result = ERROR_NESTED_FOR_OF_LOOP; fail_if_error(result); compiler->loop_index++; var_frame = _yr_compiler_get_var_frame(compiler); yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 1, NULL, NULL); yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 2, NULL, NULL); // Pop the first string. yr_parser_emit_with_arg( yyscanner, OP_POP_M, var_frame, &ref, NULL); compiler->loop_for_of_var_index = var_frame; compiler->loop[compiler->loop_index].start_ref = ref; compiler->loop[compiler->loop_index].vars_count = 0; compiler->loop[compiler->loop_index].vars_internal_count = \ YR_INTERNAL_LOOP_VARS; } #line 3365 "grammar.c" /* yacc.c:1663 */ break; case 92: #line 1714 "grammar.y" /* yacc.c:1663 */ { int var_frame = 0; compiler->loop_for_of_var_index = -1; var_frame = _yr_compiler_get_var_frame(compiler); // Increment counter by the value returned by the // boolean expression (0 or 1). If the boolean expression // returned YR_UNDEFINED the OP_ADD_M won't do anything. yr_parser_emit_with_arg( yyscanner, OP_ADD_M, var_frame + 1, NULL, NULL); // Increment iterations counter. yr_parser_emit_with_arg( yyscanner, OP_INCR_M, var_frame + 2, NULL, NULL); int32_t jmp_offset = \ compiler->loop[compiler->loop_index].start_ref.offset - yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION); // If next string is not undefined, go back to the // beginning of the loop. yr_parser_emit_with_arg_int32( yyscanner, OP_JNUNDEF, jmp_offset, NULL, NULL); // Pop end-of-list marker. yr_parser_emit(yyscanner, OP_POP, NULL); // At this point the loop quantifier (any, all, 1, 2,..) // is at top of the stack. Check if the quantifier is // undefined (meaning "all") and replace it with the // iterations counter in that case. yr_parser_emit_with_arg( yyscanner, OP_SWAPUNDEF, var_frame + 2, NULL, NULL); // Compare the loop quantifier with the number of // expressions evaluating to true. yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 1, NULL, NULL); yr_parser_emit(yyscanner, OP_INT_LE, NULL); loop_vars_cleanup(compiler->loop_index); compiler->loop_index--; (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3424 "grammar.c" /* yacc.c:1663 */ break; case 93: #line 1769 "grammar.y" /* yacc.c:1663 */ { yr_parser_emit(yyscanner, OP_OF, NULL); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3434 "grammar.c" /* yacc.c:1663 */ break; case 94: #line 1775 "grammar.y" /* yacc.c:1663 */ { yr_parser_emit(yyscanner, OP_NOT, NULL); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3444 "grammar.c" /* yacc.c:1663 */ break; case 95: #line 1781 "grammar.y" /* yacc.c:1663 */ { YR_FIXUP* fixup; YR_ARENA_REF jmp_offset_ref; fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JFALSE, 0, // still don't know the jump offset, use 0 for now. NULL, &jmp_offset_ref)); // Create a fixup entry for the jump and push it in the stack. fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; } #line 3470 "grammar.c" /* yacc.c:1663 */ break; case 96: #line 1803 "grammar.y" /* yacc.c:1663 */ { YR_FIXUP* fixup; fail_if_error(yr_parser_emit(yyscanner, OP_AND, NULL)); fixup = compiler->fixup_stack_head; int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); int32_t jmp_offset = \ yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION) - fixup->ref.offset + 1; *jmp_offset_addr = jmp_offset; // Remove fixup from the stack. compiler->fixup_stack_head = fixup->next; yr_free(fixup); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3497 "grammar.c" /* yacc.c:1663 */ break; case 97: #line 1826 "grammar.y" /* yacc.c:1663 */ { YR_FIXUP* fixup; YR_ARENA_REF jmp_offset_ref; fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JTRUE, 0, // still don't know the jump destination, use 0 for now. NULL, &jmp_offset_ref)); fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; } #line 3522 "grammar.c" /* yacc.c:1663 */ break; case 98: #line 1847 "grammar.y" /* yacc.c:1663 */ { YR_FIXUP* fixup; fail_if_error(yr_parser_emit(yyscanner, OP_OR, NULL)); fixup = compiler->fixup_stack_head; int32_t jmp_offset = \ yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION) - fixup->ref.offset + 1; int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); *jmp_offset_addr = jmp_offset; // Remove fixup from the stack. compiler->fixup_stack_head = fixup->next; yr_free(fixup); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3549 "grammar.c" /* yacc.c:1663 */ break; case 99: #line 1870 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_operation( yyscanner, "<", (yyvsp[-2].expression), (yyvsp[0].expression))); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3560 "grammar.c" /* yacc.c:1663 */ break; case 100: #line 1877 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_operation( yyscanner, ">", (yyvsp[-2].expression), (yyvsp[0].expression))); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3571 "grammar.c" /* yacc.c:1663 */ break; case 101: #line 1884 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_operation( yyscanner, "<=", (yyvsp[-2].expression), (yyvsp[0].expression))); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3582 "grammar.c" /* yacc.c:1663 */ break; case 102: #line 1891 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_operation( yyscanner, ">=", (yyvsp[-2].expression), (yyvsp[0].expression))); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3593 "grammar.c" /* yacc.c:1663 */ break; case 103: #line 1898 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_operation( yyscanner, "==", (yyvsp[-2].expression), (yyvsp[0].expression))); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3604 "grammar.c" /* yacc.c:1663 */ break; case 104: #line 1905 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_reduce_operation( yyscanner, "!=", (yyvsp[-2].expression), (yyvsp[0].expression))); (yyval.expression).type = EXPRESSION_TYPE_BOOLEAN; } #line 3615 "grammar.c" /* yacc.c:1663 */ break; case 105: #line 1912 "grammar.y" /* yacc.c:1663 */ { (yyval.expression) = (yyvsp[0].expression); } #line 3623 "grammar.c" /* yacc.c:1663 */ break; case 106: #line 1916 "grammar.y" /* yacc.c:1663 */ { (yyval.expression) = (yyvsp[-1].expression); } #line 3631 "grammar.c" /* yacc.c:1663 */ break; case 107: #line 1924 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; if (yr_parser_lookup_loop_variable(yyscanner, (yyvsp[0].c_string), NULL) >= 0) { yr_compiler_set_error_extra_info(compiler, (yyvsp[0].c_string)); yr_free((yyvsp[0].c_string)); result = ERROR_DUPLICATED_LOOP_IDENTIFIER; } fail_if_error(result); loop_ctx->vars[loop_ctx->vars_count++].identifier.ptr = (yyvsp[0].c_string); assert(loop_ctx->vars_count <= YR_MAX_LOOP_VARS); } #line 3655 "grammar.c" /* yacc.c:1663 */ break; case 108: #line 1944 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; if (loop_ctx->vars_count == YR_MAX_LOOP_VARS) { yr_compiler_set_error_extra_info(compiler, "too many loop variables"); yr_free((yyvsp[0].c_string)); result = ERROR_SYNTAX_ERROR; } else if (yr_parser_lookup_loop_variable(yyscanner, (yyvsp[0].c_string), NULL) >= 0) { yr_compiler_set_error_extra_info(compiler, (yyvsp[0].c_string)); yr_free((yyvsp[0].c_string)); result = ERROR_DUPLICATED_LOOP_IDENTIFIER; } fail_if_error(result); loop_ctx->vars[loop_ctx->vars_count++].identifier.ptr = (yyvsp[0].c_string); } #line 3684 "grammar.c" /* yacc.c:1663 */ break; case 109: #line 1972 "grammar.y" /* yacc.c:1663 */ { YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; // Initially we assume that the identifier is from a non-iterable type, // this will change later if it's iterable. int result = ERROR_WRONG_TYPE; if ((yyvsp[0].expression).type == EXPRESSION_TYPE_OBJECT) { switch((yyvsp[0].expression).value.object->type) { case OBJECT_TYPE_ARRAY: // If iterating an array the loop must define a single variable // that will hold the current item. If a different number of // variables were defined that's an error. if (loop_ctx->vars_count == 1) { loop_ctx->vars[0].type = EXPRESSION_TYPE_OBJECT; loop_ctx->vars[0].value.object = \ object_as_array((yyvsp[0].expression).value.object)->prototype_item; result = yr_parser_emit(yyscanner, OP_ITER_START_ARRAY, NULL); } else { yr_compiler_set_error_extra_info_fmt( compiler, "iterator for \"%s\" yields a single item on each iteration" ", but the loop expects %d", expression_identifier((yyvsp[0].expression)), loop_ctx->vars_count); result = ERROR_SYNTAX_ERROR; } break; case OBJECT_TYPE_DICTIONARY: // If iterating a dictionary the loop must define exactly two // variables, one for the key and another for the value . If a // different number of variables were defined that's an error. if (loop_ctx->vars_count == 2) { loop_ctx->vars[0].type = EXPRESSION_TYPE_STRING; loop_ctx->vars[0].value.sized_string_ref = YR_ARENA_NULL_REF; loop_ctx->vars[1].type = EXPRESSION_TYPE_OBJECT; loop_ctx->vars[1].value.object = \ object_as_array((yyvsp[0].expression).value.object)->prototype_item; result = yr_parser_emit(yyscanner, OP_ITER_START_DICT, NULL); } else { yr_compiler_set_error_extra_info_fmt( compiler, "iterator for \"%s\" yields a key,value pair item on each iteration", expression_identifier((yyvsp[0].expression))); result = ERROR_SYNTAX_ERROR; } break; } } if (result == ERROR_WRONG_TYPE) { yr_compiler_set_error_extra_info_fmt( compiler, "identifier \"%s\" is not iterable", expression_identifier((yyvsp[0].expression))); } fail_if_error(result); } #line 3762 "grammar.c" /* yacc.c:1663 */ break; case 110: #line 2046 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; if (loop_ctx->vars_count == 1) { loop_ctx->vars[0].type = EXPRESSION_TYPE_INTEGER; loop_ctx->vars[0].value.integer = YR_UNDEFINED; } else { yr_compiler_set_error_extra_info_fmt( compiler, "iterator yields an integer on each iteration " ", but the loop expects %d", loop_ctx->vars_count); result = ERROR_SYNTAX_ERROR; } fail_if_error(result); } #line 3790 "grammar.c" /* yacc.c:1663 */ break; case 111: #line 2074 "grammar.y" /* yacc.c:1663 */ { // $2 contains the number of integers in the enumeration fail_if_error(yr_parser_emit_push_const(yyscanner, (yyvsp[-1].integer))); fail_if_error(yr_parser_emit( yyscanner, OP_ITER_START_INT_ENUM, NULL)); } #line 3802 "grammar.c" /* yacc.c:1663 */ break; case 112: #line 2082 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit( yyscanner, OP_ITER_START_INT_RANGE, NULL)); } #line 3811 "grammar.c" /* yacc.c:1663 */ break; case 113: #line 2091 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[-3].expression).type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for range's lower bound"); result = ERROR_WRONG_TYPE; } if ((yyvsp[-1].expression).type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for range's upper bound"); result = ERROR_WRONG_TYPE; } fail_if_error(result); } #line 3835 "grammar.c" /* yacc.c:1663 */ break; case 114: #line 2115 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[0].expression).type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for enumeration item"); result = ERROR_WRONG_TYPE; } fail_if_error(result); (yyval.integer) = 1; } #line 3854 "grammar.c" /* yacc.c:1663 */ break; case 115: #line 2130 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[0].expression).type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for enumeration item"); result = ERROR_WRONG_TYPE; } fail_if_error(result); (yyval.integer) = (yyvsp[-2].integer) + 1; } #line 3873 "grammar.c" /* yacc.c:1663 */ break; case 116: #line 2149 "grammar.y" /* yacc.c:1663 */ { // Push end-of-list marker yr_parser_emit_push_const(yyscanner, YR_UNDEFINED); } #line 3882 "grammar.c" /* yacc.c:1663 */ break; case 118: #line 2155 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit_push_const(yyscanner, YR_UNDEFINED)); fail_if_error(yr_parser_emit_pushes_for_strings( yyscanner, "$*")); } #line 3893 "grammar.c" /* yacc.c:1663 */ break; case 121: #line 2172 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_emit_pushes_for_strings(yyscanner, (yyvsp[0].c_string)); yr_free((yyvsp[0].c_string)); fail_if_error(result); } #line 3904 "grammar.c" /* yacc.c:1663 */ break; case 122: #line 2179 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_emit_pushes_for_strings(yyscanner, (yyvsp[0].c_string)); yr_free((yyvsp[0].c_string)); fail_if_error(result); } #line 3915 "grammar.c" /* yacc.c:1663 */ break; case 123: #line 2190 "grammar.y" /* yacc.c:1663 */ { (yyval.integer) = FOR_EXPRESSION_ANY; } #line 3923 "grammar.c" /* yacc.c:1663 */ break; case 124: #line 2194 "grammar.y" /* yacc.c:1663 */ { yr_parser_emit_push_const(yyscanner, YR_UNDEFINED); (yyval.integer) = FOR_EXPRESSION_ALL; } #line 3932 "grammar.c" /* yacc.c:1663 */ break; case 125: #line 2199 "grammar.y" /* yacc.c:1663 */ { yr_parser_emit_push_const(yyscanner, 1); (yyval.integer) = FOR_EXPRESSION_ANY; } #line 3941 "grammar.c" /* yacc.c:1663 */ break; case 126: #line 2208 "grammar.y" /* yacc.c:1663 */ { (yyval.expression) = (yyvsp[-1].expression); } #line 3949 "grammar.c" /* yacc.c:1663 */ break; case 127: #line 2212 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit( yyscanner, OP_FILESIZE, NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 3961 "grammar.c" /* yacc.c:1663 */ break; case 128: #line 2220 "grammar.y" /* yacc.c:1663 */ { yywarning(yyscanner, "Using deprecated \"entrypoint\" keyword. Use the \"entry_point\" " "function from PE module instead."); fail_if_error(yr_parser_emit( yyscanner, OP_ENTRYPOINT, NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 3977 "grammar.c" /* yacc.c:1663 */ break; case 129: #line 2232 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-1].expression), EXPRESSION_TYPE_INTEGER, "intXXXX or uintXXXX"); // _INTEGER_FUNCTION_ could be any of int8, int16, int32, uint8, // uint32, etc. $1 contains an index that added to OP_READ_INT results // in the proper OP_INTXX opcode. fail_if_error(yr_parser_emit( yyscanner, (uint8_t) (OP_READ_INT + (yyvsp[-3].integer)), NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 3995 "grammar.c" /* yacc.c:1663 */ break; case 130: #line 2246 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit_push_const(yyscanner, (yyvsp[0].integer))); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = (yyvsp[0].integer); } #line 4006 "grammar.c" /* yacc.c:1663 */ break; case 131: #line 2253 "grammar.y" /* yacc.c:1663 */ { fail_if_error(yr_parser_emit_with_arg_double( yyscanner, OP_PUSH, (yyvsp[0].double_), NULL, NULL)); (yyval.expression).type = EXPRESSION_TYPE_FLOAT; } #line 4017 "grammar.c" /* yacc.c:1663 */ break; case 132: #line 2260 "grammar.y" /* yacc.c:1663 */ { YR_ARENA_REF ref; int result = _yr_compiler_store_data( compiler, (yyvsp[0].sized_string), (yyvsp[0].sized_string)->length + sizeof(SIZED_STRING), &ref); yr_free((yyvsp[0].sized_string)); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_PUSH, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_STRING; (yyval.expression).value.sized_string_ref = ref; } #line 4046 "grammar.c" /* yacc.c:1663 */ break; case 133: #line 2285 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[0].c_string), OP_COUNT, YR_UNDEFINED); yr_free((yyvsp[0].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 4062 "grammar.c" /* yacc.c:1663 */ break; case 134: #line 2297 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[-3].c_string), OP_OFFSET, YR_UNDEFINED); yr_free((yyvsp[-3].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 4078 "grammar.c" /* yacc.c:1663 */ break; case 135: #line 2309 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_emit_push_const(yyscanner, 1); if (result == ERROR_SUCCESS) result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[0].c_string), OP_OFFSET, YR_UNDEFINED); yr_free((yyvsp[0].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 4097 "grammar.c" /* yacc.c:1663 */ break; case 136: #line 2324 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[-3].c_string), OP_LENGTH, YR_UNDEFINED); yr_free((yyvsp[-3].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 4113 "grammar.c" /* yacc.c:1663 */ break; case 137: #line 2336 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_emit_push_const(yyscanner, 1); if (result == ERROR_SUCCESS) result = yr_parser_reduce_string_identifier( yyscanner, (yyvsp[0].c_string), OP_LENGTH, YR_UNDEFINED); yr_free((yyvsp[0].c_string)); fail_if_error(result); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; } #line 4132 "grammar.c" /* yacc.c:1663 */ break; case 138: #line 2351 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; if ((yyvsp[0].expression).type == EXPRESSION_TYPE_OBJECT) { result = yr_parser_emit( yyscanner, OP_OBJ_VALUE, NULL); switch((yyvsp[0].expression).value.object->type) { case OBJECT_TYPE_INTEGER: (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = YR_UNDEFINED; break; case OBJECT_TYPE_FLOAT: (yyval.expression).type = EXPRESSION_TYPE_FLOAT; break; case OBJECT_TYPE_STRING: (yyval.expression).type = EXPRESSION_TYPE_STRING; (yyval.expression).value.sized_string_ref = YR_ARENA_NULL_REF; break; default: // In a primary expression any identifier that corresponds to an // object must be of type integer, float or string. If "foobar" is // either a function, structure, dictionary or array you can not // use it as: // condition: foobar yr_compiler_set_error_extra_info_fmt( compiler, "wrong usage of identifier \"%s\"", expression_identifier((yyvsp[0].expression))); result = ERROR_WRONG_TYPE; } } else { (yyval.expression) = (yyvsp[0].expression); } fail_if_error(result); } #line 4179 "grammar.c" /* yacc.c:1663 */ break; case 139: #line 2394 "grammar.y" /* yacc.c:1663 */ { int result = ERROR_SUCCESS; check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER | EXPRESSION_TYPE_FLOAT, "-"); if ((yyvsp[0].expression).type == EXPRESSION_TYPE_INTEGER) { (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = ((yyvsp[0].expression).value.integer == YR_UNDEFINED) ? YR_UNDEFINED : -((yyvsp[0].expression).value.integer); result = yr_parser_emit(yyscanner, OP_INT_MINUS, NULL); } else if ((yyvsp[0].expression).type == EXPRESSION_TYPE_FLOAT) { (yyval.expression).type = EXPRESSION_TYPE_FLOAT; result = yr_parser_emit(yyscanner, OP_DBL_MINUS, NULL); } fail_if_error(result); } #line 4204 "grammar.c" /* yacc.c:1663 */ break; case 140: #line 2415 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_operation( yyscanner, "+", (yyvsp[-2].expression), (yyvsp[0].expression)); if ((yyvsp[-2].expression).type == EXPRESSION_TYPE_INTEGER && (yyvsp[0].expression).type == EXPRESSION_TYPE_INTEGER) { int64_t i1 = (yyvsp[-2].expression).value.integer; int64_t i2 = (yyvsp[0].expression).value.integer; if (!IS_UNDEFINED(i1) && !IS_UNDEFINED(i2) && ( (i2 > 0 && i1 > INT64_MAX - i2) || (i2 < 0 && i1 < INT64_MIN - i2) )) { yr_compiler_set_error_extra_info_fmt( compiler, "%" PRId64 " + %" PRId64, i1, i2); result = ERROR_INTEGER_OVERFLOW; } else { (yyval.expression).value.integer = OPERATION(+, i1, i2); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; } } else { (yyval.expression).type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } #line 4243 "grammar.c" /* yacc.c:1663 */ break; case 141: #line 2450 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_operation( yyscanner, "-", (yyvsp[-2].expression), (yyvsp[0].expression)); if ((yyvsp[-2].expression).type == EXPRESSION_TYPE_INTEGER && (yyvsp[0].expression).type == EXPRESSION_TYPE_INTEGER) { int64_t i1 = (yyvsp[-2].expression).value.integer; int64_t i2 = (yyvsp[0].expression).value.integer; if (!IS_UNDEFINED(i1) && !IS_UNDEFINED(i2) && ( (i2 < 0 && i1 > INT64_MAX + i2) || (i2 > 0 && i1 < INT64_MIN + i2) )) { yr_compiler_set_error_extra_info_fmt( compiler, "%" PRId64 " - %" PRId64, i1, i2); result = ERROR_INTEGER_OVERFLOW; } else { (yyval.expression).value.integer = OPERATION(-, i1, i2); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; } } else { (yyval.expression).type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } #line 4282 "grammar.c" /* yacc.c:1663 */ break; case 142: #line 2485 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_operation( yyscanner, "*", (yyvsp[-2].expression), (yyvsp[0].expression)); if ((yyvsp[-2].expression).type == EXPRESSION_TYPE_INTEGER && (yyvsp[0].expression).type == EXPRESSION_TYPE_INTEGER) { int64_t i1 = (yyvsp[-2].expression).value.integer; int64_t i2 = (yyvsp[0].expression).value.integer; if (!IS_UNDEFINED(i1) && !IS_UNDEFINED(i2) && ( i2 != 0 && llabs(i1) > INT64_MAX / llabs(i2) )) { yr_compiler_set_error_extra_info_fmt( compiler, "%" PRId64 " * %" PRId64, i1, i2); result = ERROR_INTEGER_OVERFLOW; } else { (yyval.expression).value.integer = OPERATION(*, i1, i2); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; } } else { (yyval.expression).type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } #line 4320 "grammar.c" /* yacc.c:1663 */ break; case 143: #line 2519 "grammar.y" /* yacc.c:1663 */ { int result = yr_parser_reduce_operation( yyscanner, "\\", (yyvsp[-2].expression), (yyvsp[0].expression)); if ((yyvsp[-2].expression).type == EXPRESSION_TYPE_INTEGER && (yyvsp[0].expression).type == EXPRESSION_TYPE_INTEGER) { if ((yyvsp[0].expression).value.integer != 0) { (yyval.expression).value.integer = OPERATION(/, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; } else { result = ERROR_DIVISION_BY_ZERO; } } else { (yyval.expression).type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } #line 4349 "grammar.c" /* yacc.c:1663 */ break; case 144: #line 2544 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_INTEGER, "%"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "%"); fail_if_error(yr_parser_emit(yyscanner, OP_MOD, NULL)); if ((yyvsp[0].expression).value.integer != 0) { (yyval.expression).value.integer = OPERATION(%, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; } else { fail_if_error(ERROR_DIVISION_BY_ZERO); } } #line 4370 "grammar.c" /* yacc.c:1663 */ break; case 145: #line 2561 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_INTEGER, "^"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "^"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_XOR, NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = OPERATION(^, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); } #line 4384 "grammar.c" /* yacc.c:1663 */ break; case 146: #line 2571 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_INTEGER, "^"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "^"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_AND, NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = OPERATION(&, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); } #line 4398 "grammar.c" /* yacc.c:1663 */ break; case 147: #line 2581 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[-2].expression), EXPRESSION_TYPE_INTEGER, "|"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "|"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_OR, NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = OPERATION(|, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); } #line 4412 "grammar.c" /* yacc.c:1663 */ break; case 148: #line 2591 "grammar.y" /* yacc.c:1663 */ { check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "~"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_NOT, NULL)); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; (yyval.expression).value.integer = ((yyvsp[0].expression).value.integer == YR_UNDEFINED) ? YR_UNDEFINED : ~((yyvsp[0].expression).value.integer); } #line 4426 "grammar.c" /* yacc.c:1663 */ break; case 149: #line 2601 "grammar.y" /* yacc.c:1663 */ { int result; check_type((yyvsp[-2].expression), EXPRESSION_TYPE_INTEGER, "<<"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, "<<"); result = yr_parser_emit(yyscanner, OP_SHL, NULL); if (!IS_UNDEFINED((yyvsp[0].expression).value.integer) && (yyvsp[0].expression).value.integer < 0) result = ERROR_INVALID_OPERAND; else if (!IS_UNDEFINED((yyvsp[0].expression).value.integer) && (yyvsp[0].expression).value.integer >= 64) (yyval.expression).value.integer = 0; else (yyval.expression).value.integer = OPERATION(<<, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; fail_if_error(result); } #line 4450 "grammar.c" /* yacc.c:1663 */ break; case 150: #line 2621 "grammar.y" /* yacc.c:1663 */ { int result; check_type((yyvsp[-2].expression), EXPRESSION_TYPE_INTEGER, ">>"); check_type((yyvsp[0].expression), EXPRESSION_TYPE_INTEGER, ">>"); result = yr_parser_emit(yyscanner, OP_SHR, NULL); if (!IS_UNDEFINED((yyvsp[0].expression).value.integer) && (yyvsp[0].expression).value.integer < 0) result = ERROR_INVALID_OPERAND; else if (!IS_UNDEFINED((yyvsp[0].expression).value.integer) && (yyvsp[0].expression).value.integer >= 64) (yyval.expression).value.integer = 0; else (yyval.expression).value.integer = OPERATION(<<, (yyvsp[-2].expression).value.integer, (yyvsp[0].expression).value.integer); (yyval.expression).type = EXPRESSION_TYPE_INTEGER; fail_if_error(result); } #line 4474 "grammar.c" /* yacc.c:1663 */ break; case 151: #line 2641 "grammar.y" /* yacc.c:1663 */ { (yyval.expression) = (yyvsp[0].expression); } #line 4482 "grammar.c" /* yacc.c:1663 */ break; #line 4486 "grammar.c" /* yacc.c:1663 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (yyscanner, compiler, YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yyscanner, compiler, yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval, yyscanner, compiler); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp, yyscanner, compiler); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (yyscanner, compiler, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval, yyscanner, compiler); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp, yyscanner, compiler); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } #line 2646 "grammar.y" /* yacc.c:1907 */ yara-4.1.3/libyara/grammar.h000066400000000000000000000116521413423160300156770ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ #ifndef YY_YARA_YY_GRAMMAR_H_INCLUDED # define YY_YARA_YY_GRAMMAR_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int yara_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { _END_OF_FILE_ = 0, _END_OF_INCLUDED_FILE_ = 258, _DOT_DOT_ = 259, _RULE_ = 260, _PRIVATE_ = 261, _GLOBAL_ = 262, _META_ = 263, _STRINGS_ = 264, _CONDITION_ = 265, _IDENTIFIER_ = 266, _STRING_IDENTIFIER_ = 267, _STRING_COUNT_ = 268, _STRING_OFFSET_ = 269, _STRING_LENGTH_ = 270, _STRING_IDENTIFIER_WITH_WILDCARD_ = 271, _NUMBER_ = 272, _DOUBLE_ = 273, _INTEGER_FUNCTION_ = 274, _TEXT_STRING_ = 275, _HEX_STRING_ = 276, _REGEXP_ = 277, _ASCII_ = 278, _WIDE_ = 279, _XOR_ = 280, _BASE64_ = 281, _BASE64_WIDE_ = 282, _NOCASE_ = 283, _FULLWORD_ = 284, _AT_ = 285, _FILESIZE_ = 286, _ENTRYPOINT_ = 287, _ALL_ = 288, _ANY_ = 289, _IN_ = 290, _OF_ = 291, _FOR_ = 292, _THEM_ = 293, _MATCHES_ = 294, _CONTAINS_ = 295, _STARTSWITH_ = 296, _ENDSWITH_ = 297, _ICONTAINS_ = 298, _ISTARTSWITH_ = 299, _IENDSWITH_ = 300, _IMPORT_ = 301, _TRUE_ = 302, _FALSE_ = 303, _OR_ = 304, _AND_ = 305, _NOT_ = 306, _EQ_ = 307, _NEQ_ = 308, _LT_ = 309, _LE_ = 310, _GT_ = 311, _GE_ = 312, _SHIFT_LEFT_ = 313, _SHIFT_RIGHT_ = 314, UNARY_MINUS = 315 }; #endif /* Tokens. */ #define _END_OF_FILE_ 0 #define _END_OF_INCLUDED_FILE_ 258 #define _DOT_DOT_ 259 #define _RULE_ 260 #define _PRIVATE_ 261 #define _GLOBAL_ 262 #define _META_ 263 #define _STRINGS_ 264 #define _CONDITION_ 265 #define _IDENTIFIER_ 266 #define _STRING_IDENTIFIER_ 267 #define _STRING_COUNT_ 268 #define _STRING_OFFSET_ 269 #define _STRING_LENGTH_ 270 #define _STRING_IDENTIFIER_WITH_WILDCARD_ 271 #define _NUMBER_ 272 #define _DOUBLE_ 273 #define _INTEGER_FUNCTION_ 274 #define _TEXT_STRING_ 275 #define _HEX_STRING_ 276 #define _REGEXP_ 277 #define _ASCII_ 278 #define _WIDE_ 279 #define _XOR_ 280 #define _BASE64_ 281 #define _BASE64_WIDE_ 282 #define _NOCASE_ 283 #define _FULLWORD_ 284 #define _AT_ 285 #define _FILESIZE_ 286 #define _ENTRYPOINT_ 287 #define _ALL_ 288 #define _ANY_ 289 #define _IN_ 290 #define _OF_ 291 #define _FOR_ 292 #define _THEM_ 293 #define _MATCHES_ 294 #define _CONTAINS_ 295 #define _STARTSWITH_ 296 #define _ENDSWITH_ 297 #define _ICONTAINS_ 298 #define _ISTARTSWITH_ 299 #define _IENDSWITH_ 300 #define _IMPORT_ 301 #define _TRUE_ 302 #define _FALSE_ 303 #define _OR_ 304 #define _AND_ 305 #define _NOT_ 306 #define _EQ_ 307 #define _NEQ_ 308 #define _LT_ 309 #define _LE_ 310 #define _GT_ 311 #define _GE_ 312 #define _SHIFT_LEFT_ 313 #define _SHIFT_RIGHT_ 314 #define UNARY_MINUS 315 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 302 "grammar.y" /* yacc.c:1916 */ YR_EXPRESSION expression; SIZED_STRING* sized_string; char* c_string; int64_t integer; double double_; YR_MODIFIER modifier; YR_ARENA_REF tag; YR_ARENA_REF rule; YR_ARENA_REF meta; YR_ARENA_REF string; #line 190 "grammar.h" /* yacc.c:1916 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif int yara_yyparse (void *yyscanner, YR_COMPILER* compiler); #endif /* !YY_YARA_YY_GRAMMAR_H_INCLUDED */ yara-4.1.3/libyara/grammar.y000066400000000000000000002272001413423160300157160ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ %{ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) #define llabs _abs64 #endif #define YYERROR_VERBOSE #define YYMALLOC yr_malloc #define YYFREE yr_free #define FOR_EXPRESSION_ALL 1 #define FOR_EXPRESSION_ANY 2 #define fail_with_error(e) \ { \ compiler->last_error = e; \ yyerror(yyscanner, compiler, NULL); \ YYERROR; \ } #define fail_if_error(e) \ if (e != ERROR_SUCCESS) \ { \ fail_with_error(e); \ } \ #define check_type_with_cleanup(expression, expected_type, op, cleanup) \ if (((expression.type) & (expected_type)) == 0) \ { \ switch(expression.type) \ { \ case EXPRESSION_TYPE_INTEGER: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"integer\" for " op " operator"); \ break; \ case EXPRESSION_TYPE_FLOAT: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"float\" for " op " operator"); \ break; \ case EXPRESSION_TYPE_STRING: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"string\" for " op " operator"); \ break; \ case EXPRESSION_TYPE_BOOLEAN: \ yr_compiler_set_error_extra_info( \ compiler, "wrong type \"boolean\" for " op " operator"); \ break; \ } \ cleanup; \ compiler->last_error = ERROR_WRONG_TYPE; \ yyerror(yyscanner, compiler, NULL); \ YYERROR; \ } // check_type(expression, EXPRESSION_TYPE_INTEGER | EXPRESSION_TYPE_FLOAT) is // used to ensure that the type of "expression" is either integer or float. #define check_type(expression, expected_type, op) \ check_type_with_cleanup(expression, expected_type, op, ) #define loop_vars_cleanup(loop_index) \ { \ YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[loop_index]; \ for (int i = 0; i < loop_ctx->vars_count; i++) \ { \ yr_free((void*) loop_ctx->vars[i].identifier.ptr); \ loop_ctx->vars[i].identifier.ptr = NULL; \ loop_ctx->vars[i].identifier.ref = YR_ARENA_NULL_REF; \ } \ loop_ctx->vars_count = 0; \ } \ // Given a YR_EXPRESSION returns its identifier. It returns identifier.ptr if // not NULL and relies on identifier.ref if otherwise. #define expression_identifier(expr) \ ((expr).identifier.ptr != NULL ? \ (expr).identifier.ptr : \ (const char*) yr_arena_ref_to_ptr(compiler->arena, &(expr).identifier.ref)) #define DEFAULT_BASE64_ALPHABET \ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" %} %expect 1 // expect 1 shift/reduce conflicts // Uncomment this line to print parsing information that can be useful to // debug YARA's grammar. // %debug %name-prefix "yara_yy" %pure-parser %parse-param {void *yyscanner} %parse-param {YR_COMPILER* compiler} %lex-param {yyscan_t yyscanner} %lex-param {YR_COMPILER* compiler} // Token that marks the end of the original file. %token _END_OF_FILE_ 0 "end of file" // Token that marks the end of included files, we can't use _END_OF_FILE_ // because bison stops parsing when it sees _END_OF_FILE_, we want to be // be able to identify the point where an included file ends, but continuing // parsing any content that follows. %token _END_OF_INCLUDED_FILE_ "end of included file" %token _DOT_DOT_ ".." %token _RULE_ "" %token _PRIVATE_ "" %token _GLOBAL_ "" %token _META_ "" %token _STRINGS_ "" %token _CONDITION_ "" %token _IDENTIFIER_ "identifier" %token _STRING_IDENTIFIER_ "string identifier" %token _STRING_COUNT_ "string count" %token _STRING_OFFSET_ "string offset" %token _STRING_LENGTH_ "string length" %token _STRING_IDENTIFIER_WITH_WILDCARD_ "string identifier with wildcard" %token _NUMBER_ "integer number" %token _DOUBLE_ "floating point number" %token _INTEGER_FUNCTION_ "integer function" %token _TEXT_STRING_ "text string" %token _HEX_STRING_ "hex string" %token _REGEXP_ "regular expression" %token _ASCII_ "" %token _WIDE_ "" %token _XOR_ "" %token _BASE64_ "" %token _BASE64_WIDE_ "" %token _NOCASE_ "" %token _FULLWORD_ "" %token _AT_ "" %token _FILESIZE_ "" %token _ENTRYPOINT_ "" %token _ALL_ "" %token _ANY_ "" %token _IN_ "" %token _OF_ "" %token _FOR_ "" %token _THEM_ "" %token _MATCHES_ "" %token _CONTAINS_ "" %token _STARTSWITH_ "" %token _ENDSWITH_ "" %token _ICONTAINS_ "" %token _ISTARTSWITH_ "" %token _IENDSWITH_ "" %token _IMPORT_ "" %token _TRUE_ "" %token _FALSE_ "" %token _OR_ "" %token _AND_ "" %token _NOT_ "" %token _EQ_ "==" %token _NEQ_ "!=" %token _LT_ "<" %token _LE_ "<=" %token _GT_ ">" %token _GE_ ">=" %token _SHIFT_LEFT_ "<<" %token _SHIFT_RIGHT_ ">>" // Operator precedence and associativity. Higher precedence operators are lower // in the list. Operators that appear in the same line have the same precedence. %left _OR_ %left _AND_ %left _EQ_ _NEQ_ _CONTAINS_ _ICONTAINS_ _STARTSWITH_ _ENDSWITH_ _ISTARTSWITH_ _IENDSWITH_ _MATCHES_ %left _LT_ _LE_ _GT_ _GE_ %left '|' %left '^' %left '&' %left _SHIFT_LEFT_ _SHIFT_RIGHT_ %left '+' '-' %left '*' '\\' '%' %right _NOT_ '~' UNARY_MINUS %type rule %type strings %type string_declaration %type string_declarations %type meta %type meta_declaration %type meta_declarations %type tags %type tag_list %type string_modifier %type string_modifiers %type regexp_modifier %type regexp_modifiers %type hex_modifier %type hex_modifiers %type integer_set %type integer_enumeration %type for_expression %type rule_modifier %type rule_modifiers %type primary_expression %type boolean_expression %type expression %type identifier %type regexp %type arguments %type arguments_list %destructor { yr_free($$); $$ = NULL; } _IDENTIFIER_ %destructor { yr_free($$); $$ = NULL; } _STRING_COUNT_ %destructor { yr_free($$); $$ = NULL; } _STRING_OFFSET_ %destructor { yr_free($$); $$ = NULL; } _STRING_LENGTH_ %destructor { yr_free($$); $$ = NULL; } _STRING_IDENTIFIER_ %destructor { yr_free($$); $$ = NULL; } _STRING_IDENTIFIER_WITH_WILDCARD_ %destructor { yr_free($$); $$ = NULL; } _TEXT_STRING_ %destructor { yr_free($$); $$ = NULL; } _HEX_STRING_ %destructor { yr_free($$); $$ = NULL; } _REGEXP_ %destructor { yr_free($$); $$ = NULL; } arguments %destructor { yr_free($$); $$ = NULL; } arguments_list %destructor { if ($$.alphabet != NULL) { yr_free($$.alphabet); $$.alphabet = NULL; } } string_modifier %destructor { if ($$.alphabet != NULL) { yr_free($$.alphabet); $$.alphabet = NULL; } } string_modifiers %union { YR_EXPRESSION expression; SIZED_STRING* sized_string; char* c_string; int64_t integer; double double_; YR_MODIFIER modifier; YR_ARENA_REF tag; YR_ARENA_REF rule; YR_ARENA_REF meta; YR_ARENA_REF string; } %% rules : /* empty */ | rules rule | rules import | rules error rule /* on error skip until next rule..*/ | rules error import /* .. or import statement */ | rules error "include" /* .. or include statement */ | rules _END_OF_INCLUDED_FILE_ { _yr_compiler_pop_file_name(compiler); } ; import : _IMPORT_ _TEXT_STRING_ { int result = yr_parser_reduce_import(yyscanner, $2); yr_free($2); fail_if_error(result); } ; rule : rule_modifiers _RULE_ _IDENTIFIER_ { fail_if_error(yr_parser_reduce_rule_declaration_phase_1( yyscanner, (int32_t) $1, $3, &$$)); } tags '{' meta strings { YR_RULE* rule = (YR_RULE*) yr_arena_ref_to_ptr( compiler->arena, &$4); rule->tags = (char*) yr_arena_ref_to_ptr( compiler->arena, &$5); rule->metas = (YR_META*) yr_arena_ref_to_ptr( compiler->arena, &$7); rule->strings = (YR_STRING*) yr_arena_ref_to_ptr( compiler->arena, &$8); } condition '}' { int result = yr_parser_reduce_rule_declaration_phase_2( yyscanner, &$4); // rule created in phase 1 yr_free($3); fail_if_error(result); } ; meta : /* empty */ { $$ = YR_ARENA_NULL_REF; } | _META_ ':' meta_declarations { YR_META* meta = yr_arena_get_ptr( compiler->arena, YR_METAS_TABLE, (compiler->current_meta_idx - 1) * sizeof(YR_META)); meta->flags |= META_FLAGS_LAST_IN_RULE; $$ = $3; } ; strings : /* empty */ { $$ = YR_ARENA_NULL_REF; } | _STRINGS_ ':' string_declarations { YR_STRING* string = (YR_STRING*) yr_arena_get_ptr( compiler->arena, YR_STRINGS_TABLE, (compiler->current_string_idx - 1) * sizeof(YR_STRING)); string->flags |= STRING_FLAGS_LAST_IN_RULE; $$ = $3; } ; condition : _CONDITION_ ':' boolean_expression ; rule_modifiers : /* empty */ { $$ = 0; } | rule_modifiers rule_modifier { $$ = $1 | $2; } ; rule_modifier : _PRIVATE_ { $$ = RULE_FLAGS_PRIVATE; } | _GLOBAL_ { $$ = RULE_FLAGS_GLOBAL; } ; tags : /* empty */ { $$ = YR_ARENA_NULL_REF; } | ':' tag_list { // Tags list is represented in the arena as a sequence // of null-terminated strings, the sequence ends with an // additional null character. Here we write the ending null //character. Example: tag1\0tag2\0tag3\0\0 fail_if_error(yr_arena_write_string( yyget_extra(yyscanner)->arena, YR_SZ_POOL, "", NULL)); $$ = $2; } ; tag_list : _IDENTIFIER_ { int result = yr_arena_write_string( yyget_extra(yyscanner)->arena, YR_SZ_POOL, $1, &$$); yr_free($1); fail_if_error(result); } | tag_list _IDENTIFIER_ { YR_ARENA_REF ref; // Write the new tag identifier. int result = yr_arena_write_string( yyget_extra(yyscanner)->arena, YR_SZ_POOL, $2, &ref); yr_free($2); fail_if_error(result); // Get the address for the tag identifier just written. char* new_tag = (char*) yr_arena_ref_to_ptr( compiler->arena, &ref); // Take the address of first tag's identifier in the list. char* tag = (char*) yr_arena_ref_to_ptr( compiler->arena, &$$); // Search for duplicated tags. Tags are written one after // the other, with zeroes in between (i.e: tag1/0tag2/0tag3) // that's why can use tag < new_tag as the condition for the // loop. while (tag < new_tag) { if (strcmp(tag, new_tag) == 0) { yr_compiler_set_error_extra_info(compiler, tag); fail_with_error(ERROR_DUPLICATED_TAG_IDENTIFIER); } tag += strlen(tag) + 1; } $$ = $1; } ; meta_declarations : meta_declaration { $$ = $1; } | meta_declarations meta_declaration { $$ = $1; } ; meta_declaration : _IDENTIFIER_ '=' _TEXT_STRING_ { SIZED_STRING* sized_string = $3; int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_STRING, $1, sized_string->c_string, 0, &$$); yr_free($1); yr_free($3); fail_if_error(result); } | _IDENTIFIER_ '=' _NUMBER_ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_INTEGER, $1, NULL, $3, &$$); yr_free($1); fail_if_error(result); } | _IDENTIFIER_ '=' '-' _NUMBER_ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_INTEGER, $1, NULL, -$4, &$$); yr_free($1); fail_if_error(result); } | _IDENTIFIER_ '=' _TRUE_ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_BOOLEAN, $1, NULL, true, &$$); yr_free($1); fail_if_error(result); } | _IDENTIFIER_ '=' _FALSE_ { int result = yr_parser_reduce_meta_declaration( yyscanner, META_TYPE_BOOLEAN, $1, NULL, false, &$$); yr_free($1); fail_if_error(result); } ; string_declarations : string_declaration { $$ = $1; } | string_declarations string_declaration { $$ = $1; } ; string_declaration : _STRING_IDENTIFIER_ '=' { compiler->current_line = yyget_lineno(yyscanner); } _TEXT_STRING_ string_modifiers { int result = yr_parser_reduce_string_declaration( yyscanner, $5, $1, $4, &$$); yr_free($1); yr_free($4); yr_free($5.alphabet); fail_if_error(result); compiler->current_line = 0; } | _STRING_IDENTIFIER_ '=' { compiler->current_line = yyget_lineno(yyscanner); } _REGEXP_ regexp_modifiers { int result; $5.flags |= STRING_FLAGS_REGEXP; result = yr_parser_reduce_string_declaration( yyscanner, $5, $1, $4, &$$); yr_free($1); yr_free($4); fail_if_error(result); compiler->current_line = 0; } | _STRING_IDENTIFIER_ '=' { compiler->current_line = yyget_lineno(yyscanner); } _HEX_STRING_ hex_modifiers { int result; $5.flags |= STRING_FLAGS_HEXADECIMAL; result = yr_parser_reduce_string_declaration( yyscanner, $5, $1, $4, &$$); yr_free($1); yr_free($4); fail_if_error(result); compiler->current_line = 0; } ; string_modifiers : /* empty */ { $$.flags = 0; $$.xor_min = 0; $$.xor_max = 0; $$.alphabet = NULL; } | string_modifiers string_modifier { $$ = $1; // Only set the xor minimum and maximum if we are dealing with the // xor modifier. If we don't check for this then we can end up with // "xor wide" resulting in whatever is on the stack for "wide" // overwriting the values for xor. if ($2.flags & STRING_FLAGS_XOR) { $$.xor_min = $2.xor_min; $$.xor_max = $2.xor_max; } // Only set the base64 alphabet if we are dealing with the base64 // modifier. If we don't check for this then we can end up with // "base64 ascii" resulting in whatever is on the stack for "ascii" // overwriting the values for base64. if (($2.flags & STRING_FLAGS_BASE64) || ($2.flags & STRING_FLAGS_BASE64_WIDE)) { if ($$.alphabet != NULL) { if (ss_compare($$.alphabet, $2.alphabet) != 0) { yr_compiler_set_error_extra_info( compiler, "can not specify multiple alphabets"); yr_free($2.alphabet); yr_free($$.alphabet); fail_with_error(ERROR_INVALID_MODIFIER); } else { yr_free($2.alphabet); } } else { $$.alphabet = $2.alphabet; } } if ($$.flags & $2.flags) { if ($$.alphabet != NULL) yr_free($$.alphabet); fail_with_error(ERROR_DUPLICATED_MODIFIER); } else { $$.flags = $$.flags | $2.flags; } } ; string_modifier : _WIDE_ { $$.flags = STRING_FLAGS_WIDE; } | _ASCII_ { $$.flags = STRING_FLAGS_ASCII; } | _NOCASE_ { $$.flags = STRING_FLAGS_NO_CASE; } | _FULLWORD_ { $$.flags = STRING_FLAGS_FULL_WORD; } | _PRIVATE_ { $$.flags = STRING_FLAGS_PRIVATE; } | _XOR_ { $$.flags = STRING_FLAGS_XOR; $$.xor_min = 0; $$.xor_max = 255; } | _XOR_ '(' _NUMBER_ ')' { int result = ERROR_SUCCESS; if ($3 < 0 || $3 > 255) { yr_compiler_set_error_extra_info(compiler, "invalid xor range"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); $$.flags = STRING_FLAGS_XOR; $$.xor_min = (uint8_t) $3; $$.xor_max = (uint8_t) $3; } /* * Would love to use range here for consistency in the language but that * uses a primary expression which pushes a value on the VM stack we don't * account for. */ | _XOR_ '(' _NUMBER_ '-' _NUMBER_ ')' { int result = ERROR_SUCCESS; if ($3 < 0) { yr_compiler_set_error_extra_info( compiler, "lower bound for xor range exceeded (min: 0)"); result = ERROR_INVALID_MODIFIER; } if ($5 > 255) { yr_compiler_set_error_extra_info( compiler, "upper bound for xor range exceeded (max: 255)"); result = ERROR_INVALID_MODIFIER; } if ($3 > $5) { yr_compiler_set_error_extra_info( compiler, "xor lower bound exceeds upper bound"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); $$.flags = STRING_FLAGS_XOR; $$.xor_min = (uint8_t) $3; $$.xor_max = (uint8_t) $5; } | _BASE64_ { $$.flags = STRING_FLAGS_BASE64; $$.alphabet = ss_new(DEFAULT_BASE64_ALPHABET); } | _BASE64_ '(' _TEXT_STRING_ ')' { int result = ERROR_SUCCESS; if ($3->length != 64) { yr_free($3); yr_compiler_set_error_extra_info( compiler, "length of base64 alphabet must be 64"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); $$.flags = STRING_FLAGS_BASE64; $$.alphabet = $3; } | _BASE64_WIDE_ { $$.flags = STRING_FLAGS_BASE64_WIDE; $$.alphabet = ss_new(DEFAULT_BASE64_ALPHABET); } | _BASE64_WIDE_ '(' _TEXT_STRING_ ')' { int result = ERROR_SUCCESS; if ($3->length != 64) { yr_free($3); yr_compiler_set_error_extra_info( compiler, "length of base64 alphabet must be 64"); result = ERROR_INVALID_MODIFIER; } fail_if_error(result); $$.flags = STRING_FLAGS_BASE64_WIDE; $$.alphabet = $3; } ; regexp_modifiers : /* empty */ { $$.flags = 0; } | regexp_modifiers regexp_modifier { if ($1.flags & $2.flags) { fail_with_error(ERROR_DUPLICATED_MODIFIER); } else { $$.flags = $1.flags | $2.flags; } } ; regexp_modifier : _WIDE_ { $$.flags = STRING_FLAGS_WIDE; } | _ASCII_ { $$.flags = STRING_FLAGS_ASCII; } | _NOCASE_ { $$.flags = STRING_FLAGS_NO_CASE; } | _FULLWORD_ { $$.flags = STRING_FLAGS_FULL_WORD; } | _PRIVATE_ { $$.flags = STRING_FLAGS_PRIVATE; } ; hex_modifiers : /* empty */ { $$.flags = 0; } | hex_modifiers hex_modifier { if ($1.flags & $2.flags) { fail_with_error(ERROR_DUPLICATED_MODIFIER); } else { $$.flags = $1.flags | $2.flags; } } ; hex_modifier : _PRIVATE_ { $$.flags = STRING_FLAGS_PRIVATE; } ; identifier : _IDENTIFIER_ { YR_EXPRESSION expr; int result = ERROR_SUCCESS; int var_index = yr_parser_lookup_loop_variable(yyscanner, $1, &expr); if (var_index >= 0) { // The identifier corresponds to a loop variable. result = yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_index, NULL, NULL); // The expression associated to this identifier is the same one // associated to the loop variable. $$ = expr; } else { // Search for identifier within the global namespace, where the // externals variables reside. YR_OBJECT* object = (YR_OBJECT*) yr_hash_table_lookup( compiler->objects_table, $1, NULL); YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE)); if (object == NULL) { // If not found, search within the current namespace. object = (YR_OBJECT*) yr_hash_table_lookup( compiler->objects_table, $1, ns->name); } if (object != NULL) { YR_ARENA_REF ref; result = _yr_compiler_store_string( compiler, $1, &ref); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_OBJ_LOAD, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); $$.type = EXPRESSION_TYPE_OBJECT; $$.value.object = object; $$.identifier.ptr = NULL; $$.identifier.ref = ref; } else { uint32_t rule_idx = yr_hash_table_lookup_uint32( compiler->rules_table, $1, ns->name); if (rule_idx != UINT32_MAX) { result = yr_parser_emit_with_arg( yyscanner, OP_PUSH_RULE, rule_idx, NULL, NULL); YR_RULE* rule = _yr_compiler_get_rule_by_idx(compiler, rule_idx); yr_arena_ptr_to_ref(compiler->arena, rule->identifier, &$$.identifier.ref); $$.type = EXPRESSION_TYPE_BOOLEAN; $$.value.integer = YR_UNDEFINED; $$.identifier.ptr = NULL; } else { yr_compiler_set_error_extra_info(compiler, $1); result = ERROR_UNDEFINED_IDENTIFIER; } } } yr_free($1); fail_if_error(result); } | identifier '.' _IDENTIFIER_ { int result = ERROR_SUCCESS; YR_OBJECT* field = NULL; if ($1.type == EXPRESSION_TYPE_OBJECT && $1.value.object->type == OBJECT_TYPE_STRUCTURE) { field = yr_object_lookup_field($1.value.object, $3); if (field != NULL) { YR_ARENA_REF ref; result = _yr_compiler_store_string( compiler, $3, &ref); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_OBJ_FIELD, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); $$.type = EXPRESSION_TYPE_OBJECT; $$.value.object = field; $$.identifier.ref = ref; $$.identifier.ptr = NULL; } else { yr_compiler_set_error_extra_info(compiler, $3); result = ERROR_INVALID_FIELD_NAME; } } else { yr_compiler_set_error_extra_info( compiler, expression_identifier($1)); result = ERROR_NOT_A_STRUCTURE; } yr_free($3); fail_if_error(result); } | identifier '[' primary_expression ']' { int result = ERROR_SUCCESS; YR_OBJECT_ARRAY* array; YR_OBJECT_DICTIONARY* dict; if ($1.type == EXPRESSION_TYPE_OBJECT && $1.value.object->type == OBJECT_TYPE_ARRAY) { if ($3.type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "array indexes must be of integer type"); result = ERROR_WRONG_TYPE; } fail_if_error(result); result = yr_parser_emit( yyscanner, OP_INDEX_ARRAY, NULL); array = object_as_array($1.value.object); $$.type = EXPRESSION_TYPE_OBJECT; $$.value.object = array->prototype_item; $$.identifier.ptr = array->identifier; $$.identifier.ref = YR_ARENA_NULL_REF; } else if ($1.type == EXPRESSION_TYPE_OBJECT && $1.value.object->type == OBJECT_TYPE_DICTIONARY) { if ($3.type != EXPRESSION_TYPE_STRING) { yr_compiler_set_error_extra_info( compiler, "dictionary keys must be of string type"); result = ERROR_WRONG_TYPE; } fail_if_error(result); result = yr_parser_emit( yyscanner, OP_LOOKUP_DICT, NULL); dict = object_as_dictionary($1.value.object); $$.type = EXPRESSION_TYPE_OBJECT; $$.value.object = dict->prototype_item; $$.identifier.ptr = dict->identifier; $$.identifier.ref = YR_ARENA_NULL_REF; } else { yr_compiler_set_error_extra_info( compiler, expression_identifier($1)); result = ERROR_NOT_INDEXABLE; } fail_if_error(result); } | identifier '(' arguments ')' { YR_ARENA_REF ref; int result = ERROR_SUCCESS; YR_OBJECT_FUNCTION* function; if ($1.type == EXPRESSION_TYPE_OBJECT && $1.value.object->type == OBJECT_TYPE_FUNCTION) { result = yr_parser_check_types( compiler, object_as_function($1.value.object), $3); if (result == ERROR_SUCCESS) result = _yr_compiler_store_string( compiler, $3, &ref); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_CALL, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); function = object_as_function($1.value.object); $$.type = EXPRESSION_TYPE_OBJECT; $$.value.object = function->return_obj; $$.identifier.ref = ref; $$.identifier.ptr = NULL; } else { yr_compiler_set_error_extra_info( compiler, expression_identifier($1)); result = ERROR_NOT_A_FUNCTION; } yr_free($3); fail_if_error(result); } ; arguments : /* empty */ { $$ = yr_strdup(""); } | arguments_list { $$ = $1; } arguments_list : expression { $$ = (char*) yr_malloc(YR_MAX_FUNCTION_ARGS + 1); if ($$ == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); switch($1.type) { case EXPRESSION_TYPE_INTEGER: strlcpy($$, "i", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_FLOAT: strlcpy($$, "f", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_BOOLEAN: strlcpy($$, "b", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_STRING: strlcpy($$, "s", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_REGEXP: strlcpy($$, "r", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_UNKNOWN: yr_free($$); yr_compiler_set_error_extra_info( compiler, "unknown type for argument 1 in function call"); fail_with_error(ERROR_WRONG_TYPE); break; default: // An unknown expression type is OK iff an error ocurred. assert(compiler->last_error != ERROR_SUCCESS); } } | arguments_list ',' expression { int result = ERROR_SUCCESS; if (strlen($1) == YR_MAX_FUNCTION_ARGS) { result = ERROR_TOO_MANY_ARGUMENTS; } else { switch($3.type) { case EXPRESSION_TYPE_INTEGER: strlcat($1, "i", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_FLOAT: strlcat($1, "f", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_BOOLEAN: strlcat($1, "b", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_STRING: strlcat($1, "s", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_REGEXP: strlcat($1, "r", YR_MAX_FUNCTION_ARGS); break; case EXPRESSION_TYPE_UNKNOWN: result = ERROR_WRONG_TYPE; yr_compiler_set_error_extra_info_fmt( compiler, "unknown type for argument %zu in function call", // As we add one character per argument, the length of $1 is // the number of arguments parsed so far, and the argument // represented by is length of $1 plus one. strlen($1) + 1); break; default: // An unknown expression type is OK iff an error ocurred. assert(compiler->last_error != ERROR_SUCCESS); } } if (result != ERROR_SUCCESS) yr_free($1); fail_if_error(result); $$ = $1; } ; regexp : _REGEXP_ { YR_ARENA_REF re_ref; RE_ERROR error; int result = ERROR_SUCCESS; int re_flags = 0; if ($1->flags & SIZED_STRING_FLAGS_NO_CASE) re_flags |= RE_FLAGS_NO_CASE; if ($1->flags & SIZED_STRING_FLAGS_DOT_ALL) re_flags |= RE_FLAGS_DOT_ALL; result = yr_re_compile( $1->c_string, re_flags, compiler->arena, &re_ref, &error); yr_free($1); if (result == ERROR_INVALID_REGULAR_EXPRESSION) yr_compiler_set_error_extra_info(compiler, error.message); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_PUSH, yr_arena_ref_to_ptr(compiler->arena, &re_ref), NULL, NULL); fail_if_error(result); $$.type = EXPRESSION_TYPE_REGEXP; } ; boolean_expression : expression { if ($1.type == EXPRESSION_TYPE_STRING) { if (!YR_ARENA_IS_NULL_REF($1.value.sized_string_ref)) { SIZED_STRING* sized_string = yr_arena_ref_to_ptr( compiler->arena, &$1.value.sized_string_ref); yywarning(yyscanner, "using literal string \"%s\" in a boolean operation.", sized_string->c_string); } fail_if_error(yr_parser_emit( yyscanner, OP_STR_TO_BOOL, NULL)); } $$.type = EXPRESSION_TYPE_BOOLEAN; } ; expression : _TRUE_ { fail_if_error(yr_parser_emit_push_const(yyscanner, 1)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | _FALSE_ { fail_if_error(yr_parser_emit_push_const(yyscanner, 0)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _MATCHES_ regexp { check_type($1, EXPRESSION_TYPE_STRING, "matches"); check_type($3, EXPRESSION_TYPE_REGEXP, "matches"); fail_if_error(yr_parser_emit( yyscanner, OP_MATCHES, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _CONTAINS_ primary_expression { check_type($1, EXPRESSION_TYPE_STRING, "contains"); check_type($3, EXPRESSION_TYPE_STRING, "contains"); fail_if_error(yr_parser_emit( yyscanner, OP_CONTAINS, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _ICONTAINS_ primary_expression { check_type($1, EXPRESSION_TYPE_STRING, "icontains"); check_type($3, EXPRESSION_TYPE_STRING, "icontains"); fail_if_error(yr_parser_emit( yyscanner, OP_ICONTAINS, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _STARTSWITH_ primary_expression { check_type($1, EXPRESSION_TYPE_STRING, "startswith"); check_type($3, EXPRESSION_TYPE_STRING, "startswith"); fail_if_error(yr_parser_emit( yyscanner, OP_STARTSWITH, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _ISTARTSWITH_ primary_expression { check_type($1, EXPRESSION_TYPE_STRING, "istartswith"); check_type($3, EXPRESSION_TYPE_STRING, "istartswith"); fail_if_error(yr_parser_emit( yyscanner, OP_ISTARTSWITH, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _ENDSWITH_ primary_expression { check_type($1, EXPRESSION_TYPE_STRING, "endswith"); check_type($3, EXPRESSION_TYPE_STRING, "endswith"); fail_if_error(yr_parser_emit( yyscanner, OP_ENDSWITH, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _IENDSWITH_ primary_expression { check_type($1, EXPRESSION_TYPE_STRING, "iendswith"); check_type($3, EXPRESSION_TYPE_STRING, "iendswith"); fail_if_error(yr_parser_emit( yyscanner, OP_IENDSWITH, NULL)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | _STRING_IDENTIFIER_ { int result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_FOUND, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_BOOLEAN; } | _STRING_IDENTIFIER_ _AT_ primary_expression { int result; check_type_with_cleanup($3, EXPRESSION_TYPE_INTEGER, "at", yr_free($1)); result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_FOUND_AT, $3.value.integer); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_BOOLEAN; } | _STRING_IDENTIFIER_ _IN_ range { int result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_FOUND_IN, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_BOOLEAN; } | _FOR_ for_expression error { // Free all the loop variable identifiers, including the variables for // the current loop (represented by loop_index), and set loop_index to // -1. This is OK even if we have nested loops. If an error occurs while // parsing the inner loop, it will be propagated to the outer loop // anyways, so it's safe to do this cleanup while processing the error // for the inner loop. for (int i = 0; i <= compiler->loop_index; i++) { loop_vars_cleanup(i); } compiler->loop_index = -1; YYERROR; } | _FOR_ for_expression // // for in : () // // CLEAR_M 0 ; clear number of true results returned by // CLEAR_M 1 ; clear loop iteration counter // POP_M 2 ; takes the result of from the stack // ; and puts it in M[2], once M[0] reaches M[2] the whole // ; for expression is satisfied // ; the instructions generated by the depend // ; on the type of iterator, but they will initialize the // ; iterator and get it ready for the ITER_NEXT instruction // repeat: // ITER_NEXT ; reads the iterator object from the stack but leaves it there, // ; puts next item in the sequence in the stack, and also a TRUE // ; or a FALSE value indicating whether or not there are more items // // POP_M 3 ; pops the next item from the stack and puts it in M[3], it // // JTRUE_P epilog ; pops the boolean that tells if we already reached // ; the end of the iterator // ; here goes the code for the value of the // ; expressions ends up being at the top of the stack // // ADD_M 0 ; if was true M[0] is incremented by one, // ; this consumes the 's result from the stack // INCR_M 1 ; increments iteration counter // // PUSH_M 2 // JUNDEF_P repeat ; if M[2] is undefined it's because is "all", // ; in that case we need to repeat until there are no more items // // PUSH_M 0 ; pushes number of true results for // PUSH_M 2 ; pushes value of // // JL_P repeat ; if M[1] is less M[3] repeat // // epilog: // POP ; remove the iterator object from the stack // // PUSH_M 1 ; push iteration counter // JZ end ; if iteration counter is 0 the loop evaluates to false // ; in all cases, we jump to "end" leaving a 0 in the stack // ; that will used as the resulting false value. // POP ; the iteration counter wasn't 0, remove it from the // ; stack // // PUSH_M 0 ; pushes number of true results for // PUSH_M 2 ; pushes value of // // SWAPUNDEF 1 ; if the value at the top of the stack (M[2]) is UNDEF // ; swap the UNDEF with loop iteration counter at M[1] // // INT_GE ; compares the the number of true results returned by // ; with the value of or // ; with the number of iterations, if // ; was "all". A 1 is pushed into the stack if the former // ; is greater than or equal to the latter // end: // { // var_frame is used for accessing local variables used in this loop. // All local variables are accessed using var_frame as a reference, // like var_frame + 0, var_frame + 1, etc. Here we initialize var_frame // with the correct value, which depends on the number of variables // defined by any outer loops. int var_frame; int result = ERROR_SUCCESS; if (compiler->loop_index + 1 == YR_MAX_LOOP_NESTING) result = ERROR_LOOP_NESTING_LIMIT_EXCEEDED; fail_if_error(result); compiler->loop_index++; // This loop uses internal variables besides the ones explicitly // defined by the user. compiler->loop[compiler->loop_index].vars_internal_count = \ YR_INTERNAL_LOOP_VARS; // Initialize the number of variables, this number will be incremented // as variable declaration are processed by for_variables. compiler->loop[compiler->loop_index].vars_count = 0; var_frame = _yr_compiler_get_var_frame(compiler); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_POP_M, var_frame + 2, NULL, NULL)); } for_variables _IN_ iterator ':' { YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; YR_FIXUP* fixup; YR_ARENA_REF loop_start_ref; YR_ARENA_REF jmp_offset_ref; int var_frame = _yr_compiler_get_var_frame(compiler); fail_if_error(yr_parser_emit( yyscanner, OP_ITER_NEXT, &loop_start_ref)); // For each variable generate an instruction that pops the value from // the stack and store it into one memory slot starting at var_frame + // YR_INTERNAL_LOOP_VARS because the first YR_INTERNAL_LOOP_VARS slots // in the frame are for the internal variables. for (int i = 0; i < loop_ctx->vars_count; i++) { fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_POP_M, var_frame + YR_INTERNAL_LOOP_VARS + i, NULL, NULL)); } fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JTRUE_P, 0, // still don't know the jump offset, use 0 for now. NULL, &jmp_offset_ref)); // We still don't know the jump's target, so we push a fixup entry // in the stack, so that the jump's offset can be set once we know it. fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; loop_ctx->start_ref = loop_start_ref; } '(' boolean_expression ')' { int32_t jmp_offset; YR_FIXUP* fixup; YR_ARENA_REF pop_ref; YR_ARENA_REF jmp_offset_ref; int var_frame = _yr_compiler_get_var_frame(compiler); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_ADD_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_INCR_M, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 2, NULL, NULL)); jmp_offset = \ compiler->loop[compiler->loop_index].start_ref.offset - yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION); fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JUNDEF_P, jmp_offset, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 2, NULL, NULL)); jmp_offset = \ compiler->loop[compiler->loop_index].start_ref.offset - yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION); fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JL_P, jmp_offset, NULL, NULL)); fail_if_error(yr_parser_emit( yyscanner, OP_POP, &pop_ref)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JZ, 0, // still don't know the jump offset, use 0 for now. NULL, &jmp_offset_ref)); fail_if_error(yr_parser_emit( yyscanner, OP_POP, NULL)); // Pop from the stack the fixup entry containing the reference to // the jump offset that needs to be fixed. fixup = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup->next; // The fixup entry has a reference to the jump offset that need // to be fixed, convert the address into a pointer. int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); // The reference in the fixup entry points to the jump's offset // but the jump instruction is one byte before, that's why we add // one to the offset. jmp_offset = pop_ref.offset - fixup->ref.offset + 1; // Fix the jump's offset. *jmp_offset_addr = jmp_offset; yr_free(fixup); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 0, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 2, NULL, NULL)); fail_if_error(yr_parser_emit_with_arg( yyscanner, OP_SWAPUNDEF, var_frame + 1, NULL, NULL)); fail_if_error(yr_parser_emit( yyscanner, OP_INT_GE, NULL)); jmp_offset = \ yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION) - jmp_offset_ref.offset + 1; jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &jmp_offset_ref); *jmp_offset_addr = jmp_offset; loop_vars_cleanup(compiler->loop_index); compiler->loop_index--; $$.type = EXPRESSION_TYPE_BOOLEAN; } | _FOR_ for_expression _OF_ string_set ':' { YR_ARENA_REF ref; int result = ERROR_SUCCESS; int var_frame; if (compiler->loop_index + 1 == YR_MAX_LOOP_NESTING) result = ERROR_LOOP_NESTING_LIMIT_EXCEEDED; if (compiler->loop_for_of_var_index != -1) result = ERROR_NESTED_FOR_OF_LOOP; fail_if_error(result); compiler->loop_index++; var_frame = _yr_compiler_get_var_frame(compiler); yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 1, NULL, NULL); yr_parser_emit_with_arg( yyscanner, OP_CLEAR_M, var_frame + 2, NULL, NULL); // Pop the first string. yr_parser_emit_with_arg( yyscanner, OP_POP_M, var_frame, &ref, NULL); compiler->loop_for_of_var_index = var_frame; compiler->loop[compiler->loop_index].start_ref = ref; compiler->loop[compiler->loop_index].vars_count = 0; compiler->loop[compiler->loop_index].vars_internal_count = \ YR_INTERNAL_LOOP_VARS; } '(' boolean_expression ')' { int var_frame = 0; compiler->loop_for_of_var_index = -1; var_frame = _yr_compiler_get_var_frame(compiler); // Increment counter by the value returned by the // boolean expression (0 or 1). If the boolean expression // returned YR_UNDEFINED the OP_ADD_M won't do anything. yr_parser_emit_with_arg( yyscanner, OP_ADD_M, var_frame + 1, NULL, NULL); // Increment iterations counter. yr_parser_emit_with_arg( yyscanner, OP_INCR_M, var_frame + 2, NULL, NULL); int32_t jmp_offset = \ compiler->loop[compiler->loop_index].start_ref.offset - yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION); // If next string is not undefined, go back to the // beginning of the loop. yr_parser_emit_with_arg_int32( yyscanner, OP_JNUNDEF, jmp_offset, NULL, NULL); // Pop end-of-list marker. yr_parser_emit(yyscanner, OP_POP, NULL); // At this point the loop quantifier (any, all, 1, 2,..) // is at top of the stack. Check if the quantifier is // undefined (meaning "all") and replace it with the // iterations counter in that case. yr_parser_emit_with_arg( yyscanner, OP_SWAPUNDEF, var_frame + 2, NULL, NULL); // Compare the loop quantifier with the number of // expressions evaluating to true. yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, var_frame + 1, NULL, NULL); yr_parser_emit(yyscanner, OP_INT_LE, NULL); loop_vars_cleanup(compiler->loop_index); compiler->loop_index--; $$.type = EXPRESSION_TYPE_BOOLEAN; } | for_expression _OF_ string_set { yr_parser_emit(yyscanner, OP_OF, NULL); $$.type = EXPRESSION_TYPE_BOOLEAN; } | _NOT_ boolean_expression { yr_parser_emit(yyscanner, OP_NOT, NULL); $$.type = EXPRESSION_TYPE_BOOLEAN; } | boolean_expression _AND_ { YR_FIXUP* fixup; YR_ARENA_REF jmp_offset_ref; fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JFALSE, 0, // still don't know the jump offset, use 0 for now. NULL, &jmp_offset_ref)); // Create a fixup entry for the jump and push it in the stack. fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; } boolean_expression { YR_FIXUP* fixup; fail_if_error(yr_parser_emit(yyscanner, OP_AND, NULL)); fixup = compiler->fixup_stack_head; int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); int32_t jmp_offset = \ yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION) - fixup->ref.offset + 1; *jmp_offset_addr = jmp_offset; // Remove fixup from the stack. compiler->fixup_stack_head = fixup->next; yr_free(fixup); $$.type = EXPRESSION_TYPE_BOOLEAN; } | boolean_expression _OR_ { YR_FIXUP* fixup; YR_ARENA_REF jmp_offset_ref; fail_if_error(yr_parser_emit_with_arg_int32( yyscanner, OP_JTRUE, 0, // still don't know the jump destination, use 0 for now. NULL, &jmp_offset_ref)); fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) fail_with_error(ERROR_INSUFFICIENT_MEMORY); fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; } boolean_expression { YR_FIXUP* fixup; fail_if_error(yr_parser_emit(yyscanner, OP_OR, NULL)); fixup = compiler->fixup_stack_head; int32_t jmp_offset = \ yr_arena_get_current_offset(compiler->arena, YR_CODE_SECTION) - fixup->ref.offset + 1; int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); *jmp_offset_addr = jmp_offset; // Remove fixup from the stack. compiler->fixup_stack_head = fixup->next; yr_free(fixup); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _LT_ primary_expression { fail_if_error(yr_parser_reduce_operation( yyscanner, "<", $1, $3)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _GT_ primary_expression { fail_if_error(yr_parser_reduce_operation( yyscanner, ">", $1, $3)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _LE_ primary_expression { fail_if_error(yr_parser_reduce_operation( yyscanner, "<=", $1, $3)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _GE_ primary_expression { fail_if_error(yr_parser_reduce_operation( yyscanner, ">=", $1, $3)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _EQ_ primary_expression { fail_if_error(yr_parser_reduce_operation( yyscanner, "==", $1, $3)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression _NEQ_ primary_expression { fail_if_error(yr_parser_reduce_operation( yyscanner, "!=", $1, $3)); $$.type = EXPRESSION_TYPE_BOOLEAN; } | primary_expression { $$ = $1; } |'(' expression ')' { $$ = $2; } ; for_variables : _IDENTIFIER_ { int result = ERROR_SUCCESS; YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; if (yr_parser_lookup_loop_variable(yyscanner, $1, NULL) >= 0) { yr_compiler_set_error_extra_info(compiler, $1); yr_free($1); result = ERROR_DUPLICATED_LOOP_IDENTIFIER; } fail_if_error(result); loop_ctx->vars[loop_ctx->vars_count++].identifier.ptr = $1; assert(loop_ctx->vars_count <= YR_MAX_LOOP_VARS); } | for_variables ',' _IDENTIFIER_ { int result = ERROR_SUCCESS; YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; if (loop_ctx->vars_count == YR_MAX_LOOP_VARS) { yr_compiler_set_error_extra_info(compiler, "too many loop variables"); yr_free($3); result = ERROR_SYNTAX_ERROR; } else if (yr_parser_lookup_loop_variable(yyscanner, $3, NULL) >= 0) { yr_compiler_set_error_extra_info(compiler, $3); yr_free($3); result = ERROR_DUPLICATED_LOOP_IDENTIFIER; } fail_if_error(result); loop_ctx->vars[loop_ctx->vars_count++].identifier.ptr = $3; } ; iterator : identifier { YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; // Initially we assume that the identifier is from a non-iterable type, // this will change later if it's iterable. int result = ERROR_WRONG_TYPE; if ($1.type == EXPRESSION_TYPE_OBJECT) { switch($1.value.object->type) { case OBJECT_TYPE_ARRAY: // If iterating an array the loop must define a single variable // that will hold the current item. If a different number of // variables were defined that's an error. if (loop_ctx->vars_count == 1) { loop_ctx->vars[0].type = EXPRESSION_TYPE_OBJECT; loop_ctx->vars[0].value.object = \ object_as_array($1.value.object)->prototype_item; result = yr_parser_emit(yyscanner, OP_ITER_START_ARRAY, NULL); } else { yr_compiler_set_error_extra_info_fmt( compiler, "iterator for \"%s\" yields a single item on each iteration" ", but the loop expects %d", expression_identifier($1), loop_ctx->vars_count); result = ERROR_SYNTAX_ERROR; } break; case OBJECT_TYPE_DICTIONARY: // If iterating a dictionary the loop must define exactly two // variables, one for the key and another for the value . If a // different number of variables were defined that's an error. if (loop_ctx->vars_count == 2) { loop_ctx->vars[0].type = EXPRESSION_TYPE_STRING; loop_ctx->vars[0].value.sized_string_ref = YR_ARENA_NULL_REF; loop_ctx->vars[1].type = EXPRESSION_TYPE_OBJECT; loop_ctx->vars[1].value.object = \ object_as_array($1.value.object)->prototype_item; result = yr_parser_emit(yyscanner, OP_ITER_START_DICT, NULL); } else { yr_compiler_set_error_extra_info_fmt( compiler, "iterator for \"%s\" yields a key,value pair item on each iteration", expression_identifier($1)); result = ERROR_SYNTAX_ERROR; } break; } } if (result == ERROR_WRONG_TYPE) { yr_compiler_set_error_extra_info_fmt( compiler, "identifier \"%s\" is not iterable", expression_identifier($1)); } fail_if_error(result); } | integer_set { int result = ERROR_SUCCESS; YR_LOOP_CONTEXT* loop_ctx = &compiler->loop[compiler->loop_index]; if (loop_ctx->vars_count == 1) { loop_ctx->vars[0].type = EXPRESSION_TYPE_INTEGER; loop_ctx->vars[0].value.integer = YR_UNDEFINED; } else { yr_compiler_set_error_extra_info_fmt( compiler, "iterator yields an integer on each iteration " ", but the loop expects %d", loop_ctx->vars_count); result = ERROR_SYNTAX_ERROR; } fail_if_error(result); } ; integer_set : '(' integer_enumeration ')' { // $2 contains the number of integers in the enumeration fail_if_error(yr_parser_emit_push_const(yyscanner, $2)); fail_if_error(yr_parser_emit( yyscanner, OP_ITER_START_INT_ENUM, NULL)); } | range { fail_if_error(yr_parser_emit( yyscanner, OP_ITER_START_INT_RANGE, NULL)); } ; range : '(' primary_expression _DOT_DOT_ primary_expression ')' { int result = ERROR_SUCCESS; if ($2.type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for range's lower bound"); result = ERROR_WRONG_TYPE; } if ($4.type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for range's upper bound"); result = ERROR_WRONG_TYPE; } fail_if_error(result); } ; integer_enumeration : primary_expression { int result = ERROR_SUCCESS; if ($1.type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for enumeration item"); result = ERROR_WRONG_TYPE; } fail_if_error(result); $$ = 1; } | integer_enumeration ',' primary_expression { int result = ERROR_SUCCESS; if ($3.type != EXPRESSION_TYPE_INTEGER) { yr_compiler_set_error_extra_info( compiler, "wrong type for enumeration item"); result = ERROR_WRONG_TYPE; } fail_if_error(result); $$ = $1 + 1; } ; string_set : '(' { // Push end-of-list marker yr_parser_emit_push_const(yyscanner, YR_UNDEFINED); } string_enumeration ')' | _THEM_ { fail_if_error(yr_parser_emit_push_const(yyscanner, YR_UNDEFINED)); fail_if_error(yr_parser_emit_pushes_for_strings( yyscanner, "$*")); } ; string_enumeration : string_enumeration_item | string_enumeration ',' string_enumeration_item ; string_enumeration_item : _STRING_IDENTIFIER_ { int result = yr_parser_emit_pushes_for_strings(yyscanner, $1); yr_free($1); fail_if_error(result); } | _STRING_IDENTIFIER_WITH_WILDCARD_ { int result = yr_parser_emit_pushes_for_strings(yyscanner, $1); yr_free($1); fail_if_error(result); } ; for_expression : primary_expression { $$ = FOR_EXPRESSION_ANY; } | _ALL_ { yr_parser_emit_push_const(yyscanner, YR_UNDEFINED); $$ = FOR_EXPRESSION_ALL; } | _ANY_ { yr_parser_emit_push_const(yyscanner, 1); $$ = FOR_EXPRESSION_ANY; } ; primary_expression : '(' primary_expression ')' { $$ = $2; } | _FILESIZE_ { fail_if_error(yr_parser_emit( yyscanner, OP_FILESIZE, NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _ENTRYPOINT_ { yywarning(yyscanner, "Using deprecated \"entrypoint\" keyword. Use the \"entry_point\" " "function from PE module instead."); fail_if_error(yr_parser_emit( yyscanner, OP_ENTRYPOINT, NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _INTEGER_FUNCTION_ '(' primary_expression ')' { check_type($3, EXPRESSION_TYPE_INTEGER, "intXXXX or uintXXXX"); // _INTEGER_FUNCTION_ could be any of int8, int16, int32, uint8, // uint32, etc. $1 contains an index that added to OP_READ_INT results // in the proper OP_INTXX opcode. fail_if_error(yr_parser_emit( yyscanner, (uint8_t) (OP_READ_INT + $1), NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _NUMBER_ { fail_if_error(yr_parser_emit_push_const(yyscanner, $1)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = $1; } | _DOUBLE_ { fail_if_error(yr_parser_emit_with_arg_double( yyscanner, OP_PUSH, $1, NULL, NULL)); $$.type = EXPRESSION_TYPE_FLOAT; } | _TEXT_STRING_ { YR_ARENA_REF ref; int result = _yr_compiler_store_data( compiler, $1, $1->length + sizeof(SIZED_STRING), &ref); yr_free($1); if (result == ERROR_SUCCESS) result = yr_parser_emit_with_arg_reloc( yyscanner, OP_PUSH, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL); fail_if_error(result); $$.type = EXPRESSION_TYPE_STRING; $$.value.sized_string_ref = ref; } | _STRING_COUNT_ { int result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_COUNT, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _STRING_OFFSET_ '[' primary_expression ']' { int result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_OFFSET, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _STRING_OFFSET_ { int result = yr_parser_emit_push_const(yyscanner, 1); if (result == ERROR_SUCCESS) result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_OFFSET, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _STRING_LENGTH_ '[' primary_expression ']' { int result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_LENGTH, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | _STRING_LENGTH_ { int result = yr_parser_emit_push_const(yyscanner, 1); if (result == ERROR_SUCCESS) result = yr_parser_reduce_string_identifier( yyscanner, $1, OP_LENGTH, YR_UNDEFINED); yr_free($1); fail_if_error(result); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; } | identifier { int result = ERROR_SUCCESS; if ($1.type == EXPRESSION_TYPE_OBJECT) { result = yr_parser_emit( yyscanner, OP_OBJ_VALUE, NULL); switch($1.value.object->type) { case OBJECT_TYPE_INTEGER: $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = YR_UNDEFINED; break; case OBJECT_TYPE_FLOAT: $$.type = EXPRESSION_TYPE_FLOAT; break; case OBJECT_TYPE_STRING: $$.type = EXPRESSION_TYPE_STRING; $$.value.sized_string_ref = YR_ARENA_NULL_REF; break; default: // In a primary expression any identifier that corresponds to an // object must be of type integer, float or string. If "foobar" is // either a function, structure, dictionary or array you can not // use it as: // condition: foobar yr_compiler_set_error_extra_info_fmt( compiler, "wrong usage of identifier \"%s\"", expression_identifier($1)); result = ERROR_WRONG_TYPE; } } else { $$ = $1; } fail_if_error(result); } | '-' primary_expression %prec UNARY_MINUS { int result = ERROR_SUCCESS; check_type($2, EXPRESSION_TYPE_INTEGER | EXPRESSION_TYPE_FLOAT, "-"); if ($2.type == EXPRESSION_TYPE_INTEGER) { $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = ($2.value.integer == YR_UNDEFINED) ? YR_UNDEFINED : -($2.value.integer); result = yr_parser_emit(yyscanner, OP_INT_MINUS, NULL); } else if ($2.type == EXPRESSION_TYPE_FLOAT) { $$.type = EXPRESSION_TYPE_FLOAT; result = yr_parser_emit(yyscanner, OP_DBL_MINUS, NULL); } fail_if_error(result); } | primary_expression '+' primary_expression { int result = yr_parser_reduce_operation( yyscanner, "+", $1, $3); if ($1.type == EXPRESSION_TYPE_INTEGER && $3.type == EXPRESSION_TYPE_INTEGER) { int64_t i1 = $1.value.integer; int64_t i2 = $3.value.integer; if (!IS_UNDEFINED(i1) && !IS_UNDEFINED(i2) && ( (i2 > 0 && i1 > INT64_MAX - i2) || (i2 < 0 && i1 < INT64_MIN - i2) )) { yr_compiler_set_error_extra_info_fmt( compiler, "%" PRId64 " + %" PRId64, i1, i2); result = ERROR_INTEGER_OVERFLOW; } else { $$.value.integer = OPERATION(+, i1, i2); $$.type = EXPRESSION_TYPE_INTEGER; } } else { $$.type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } | primary_expression '-' primary_expression { int result = yr_parser_reduce_operation( yyscanner, "-", $1, $3); if ($1.type == EXPRESSION_TYPE_INTEGER && $3.type == EXPRESSION_TYPE_INTEGER) { int64_t i1 = $1.value.integer; int64_t i2 = $3.value.integer; if (!IS_UNDEFINED(i1) && !IS_UNDEFINED(i2) && ( (i2 < 0 && i1 > INT64_MAX + i2) || (i2 > 0 && i1 < INT64_MIN + i2) )) { yr_compiler_set_error_extra_info_fmt( compiler, "%" PRId64 " - %" PRId64, i1, i2); result = ERROR_INTEGER_OVERFLOW; } else { $$.value.integer = OPERATION(-, i1, i2); $$.type = EXPRESSION_TYPE_INTEGER; } } else { $$.type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } | primary_expression '*' primary_expression { int result = yr_parser_reduce_operation( yyscanner, "*", $1, $3); if ($1.type == EXPRESSION_TYPE_INTEGER && $3.type == EXPRESSION_TYPE_INTEGER) { int64_t i1 = $1.value.integer; int64_t i2 = $3.value.integer; if (!IS_UNDEFINED(i1) && !IS_UNDEFINED(i2) && ( i2 != 0 && llabs(i1) > INT64_MAX / llabs(i2) )) { yr_compiler_set_error_extra_info_fmt( compiler, "%" PRId64 " * %" PRId64, i1, i2); result = ERROR_INTEGER_OVERFLOW; } else { $$.value.integer = OPERATION(*, i1, i2); $$.type = EXPRESSION_TYPE_INTEGER; } } else { $$.type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } | primary_expression '\\' primary_expression { int result = yr_parser_reduce_operation( yyscanner, "\\", $1, $3); if ($1.type == EXPRESSION_TYPE_INTEGER && $3.type == EXPRESSION_TYPE_INTEGER) { if ($3.value.integer != 0) { $$.value.integer = OPERATION(/, $1.value.integer, $3.value.integer); $$.type = EXPRESSION_TYPE_INTEGER; } else { result = ERROR_DIVISION_BY_ZERO; } } else { $$.type = EXPRESSION_TYPE_FLOAT; } fail_if_error(result); } | primary_expression '%' primary_expression { check_type($1, EXPRESSION_TYPE_INTEGER, "%"); check_type($3, EXPRESSION_TYPE_INTEGER, "%"); fail_if_error(yr_parser_emit(yyscanner, OP_MOD, NULL)); if ($3.value.integer != 0) { $$.value.integer = OPERATION(%, $1.value.integer, $3.value.integer); $$.type = EXPRESSION_TYPE_INTEGER; } else { fail_if_error(ERROR_DIVISION_BY_ZERO); } } | primary_expression '^' primary_expression { check_type($1, EXPRESSION_TYPE_INTEGER, "^"); check_type($3, EXPRESSION_TYPE_INTEGER, "^"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_XOR, NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = OPERATION(^, $1.value.integer, $3.value.integer); } | primary_expression '&' primary_expression { check_type($1, EXPRESSION_TYPE_INTEGER, "^"); check_type($3, EXPRESSION_TYPE_INTEGER, "^"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_AND, NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = OPERATION(&, $1.value.integer, $3.value.integer); } | primary_expression '|' primary_expression { check_type($1, EXPRESSION_TYPE_INTEGER, "|"); check_type($3, EXPRESSION_TYPE_INTEGER, "|"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_OR, NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = OPERATION(|, $1.value.integer, $3.value.integer); } | '~' primary_expression { check_type($2, EXPRESSION_TYPE_INTEGER, "~"); fail_if_error(yr_parser_emit(yyscanner, OP_BITWISE_NOT, NULL)); $$.type = EXPRESSION_TYPE_INTEGER; $$.value.integer = ($2.value.integer == YR_UNDEFINED) ? YR_UNDEFINED : ~($2.value.integer); } | primary_expression _SHIFT_LEFT_ primary_expression { int result; check_type($1, EXPRESSION_TYPE_INTEGER, "<<"); check_type($3, EXPRESSION_TYPE_INTEGER, "<<"); result = yr_parser_emit(yyscanner, OP_SHL, NULL); if (!IS_UNDEFINED($3.value.integer) && $3.value.integer < 0) result = ERROR_INVALID_OPERAND; else if (!IS_UNDEFINED($3.value.integer) && $3.value.integer >= 64) $$.value.integer = 0; else $$.value.integer = OPERATION(<<, $1.value.integer, $3.value.integer); $$.type = EXPRESSION_TYPE_INTEGER; fail_if_error(result); } | primary_expression _SHIFT_RIGHT_ primary_expression { int result; check_type($1, EXPRESSION_TYPE_INTEGER, ">>"); check_type($3, EXPRESSION_TYPE_INTEGER, ">>"); result = yr_parser_emit(yyscanner, OP_SHR, NULL); if (!IS_UNDEFINED($3.value.integer) && $3.value.integer < 0) result = ERROR_INVALID_OPERAND; else if (!IS_UNDEFINED($3.value.integer) && $3.value.integer >= 64) $$.value.integer = 0; else $$.value.integer = OPERATION(<<, $1.value.integer, $3.value.integer); $$.type = EXPRESSION_TYPE_INTEGER; fail_if_error(result); } | regexp { $$ = $1; } ; %% yara-4.1.3/libyara/hash.c000066400000000000000000000267031413423160300151720ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include // Constant-time left rotate that does not invoke undefined behavior. // http://blog.regehr.org/archives/1063 static uint32_t rotl32(uint32_t x, uint32_t shift) { assert(shift < 32); return (x << shift) | (x >> (-shift & 31)); } #define ROTATE_INT32(x, shift) rotl32(x, shift % 32) uint32_t byte_to_int32[] = { 0xC3113E7F, 0x4C353C5F, 0x7423810B, 0x258D264E, 0xDAD39DED, 0x75D0B694, 0x98CE1216, 0x93334482, 0xC5C48EA5, 0xF57E0E8B, 0x5D7F3723, 0x396B1B24, 0xA8883D9F, 0xB2A74A00, 0xF8E171AE, 0x3F01FBAB, 0x5C1840CB, 0xDDD833C4, 0x8D8CCA34, 0x32EF223A, 0x1A05B871, 0x9A9B6BFC, 0x50406A0C, 0xE7E1FC04, 0x5E07D7F6, 0x80B83660, 0x20892A62, 0xB2C6FEA6, 0x6CEC7CAA, 0x182F764B, 0x3B0353E7, 0x57FC2520, 0x4B6812D4, 0xACB654E4, 0x23C75C04, 0xB1DCD731, 0xE3AF0733, 0xF2366D39, 0xC729671B, 0xFF3BE6F2, 0xABA37E34, 0x3CDAFA38, 0xAAD18D03, 0xA8D35345, 0x08E9A92C, 0xF9324059, 0x42D821BE, 0x1BC152DD, 0x5588811C, 0x874A1F9A, 0x6E83E9CD, 0xDA6F3AF8, 0x965D4670, 0xA7A565C0, 0x68D8A9AF, 0xFC8FD8FD, 0x8FF99FF9, 0x4C9B42AE, 0x2D066A8D, 0x4D1802F7, 0x557032B2, 0x12BCF371, 0xDC29D5AE, 0x72EA361F, 0xE2835B0B, 0xDFC58966, 0x13B0F34D, 0x3FA02BCD, 0xBF282E3D, 0x7DC877F5, 0xF4848A32, 0x861E35F5, 0x7FFA0D7F, 0x515F2E4E, 0x6B235D5C, 0x55F46E24, 0x35AD2C99, 0x072654A8, 0x05163F0F, 0x9317B11A, 0xAED1FC10, 0x989444F0, 0xDB3E1814, 0x446C0CF1, 0x660BF511, 0x2F227D3A, 0xFDBA0539, 0xC649E621, 0x5204D7CE, 0x5FA386D0, 0xE5F22005, 0x97B6C8A1, 0x4AB69EC2, 0x5C7CA70D, 0x39A48EC6, 0x7BACF378, 0x8D0ED3D1, 0xE39DE582, 0xC5FBE2AB, 0x37E3D2D0, 0x06F44724, 0x73144144, 0xBA57E905, 0xB05B4307, 0xAEED8D97, 0xA68CCAC4, 0xE30DA57E, 0xED0F194B, 0x8C2B9B7A, 0x814575D5, 0x79588493, 0x81D3712A, 0x3FA892F2, 0x80F0BB94, 0x44EAF51A, 0x4E05F1D4, 0xFC69F858, 0x775E8D60, 0x22B20DD7, 0x170A87EA, 0x1077DE52, 0x3D5EC9FB, 0x0B6EB1E5, 0xF2F9CCAF, 0xA76C7DEB, 0xD8C2D873, 0xF438C592, 0x6239FEEC, 0x26D3D2A9, 0x30F6FADF, 0x4B2984CC, 0x6257F3DA, 0x0E0583E2, 0x143E5E61, 0xBB2732BF, 0x9653217A, 0x027A84EA, 0x95C9AE8B, 0x89B8B82B, 0x9F286485, 0x29F622FE, 0x52A3196B, 0x8392D95F, 0x33A79167, 0xF5DEE92A, 0x6E397DB9, 0x11931C01, 0x8DD2CD3B, 0xF9E6003D, 0xAB955AF4, 0xD38725F9, 0xDCF6F8AE, 0x7667A958, 0xE67AD995, 0xB7CF979A, 0xD88EBE5B, 0x5BA889F0, 0x078BDD90, 0x447238F9, 0x3135F672, 0x187B95A8, 0x0B7D5751, 0xACD59D2A, 0x9C5D1929, 0x579E5022, 0xEA90499B, 0x59901800, 0x82237DB5, 0x7A375509, 0xACA9A22A, 0xEC96E649, 0x69339DB0, 0x081D0D9B, 0xD72FB8B9, 0xA4184653, 0xC057321D, 0xED19CAB9, 0xB48F1E3E, 0xB9DAC51E, 0xDAED2FC7, 0x7598CBBD, 0x208DF346, 0x044BE6EC, 0x1C63E6EB, 0xA15F64C1, 0xE024A061, 0x68309584, 0x0758A68D, 0xF274E9AE, 0x0ABEA0CC, 0xED4FB267, 0x63D6EC46, 0x9F28E026, 0xF0694A17, 0x9D6E9115, 0xC4600FAD, 0x5B121E99, 0xD6B4A13B, 0xF5364B8A, 0x8514B254, 0x0182F8DD, 0xDB09F90B, 0x78C70B32, 0xD8EC3B02, 0x8CD7084D, 0xA4439838, 0x72F35A3D, 0x200B48A5, 0xE2351444, 0xA5552F5F, 0xD8C1E746, 0x0FE5EF3C, 0xB6A47063, 0x61F4E68B, 0x08FED99B, 0x7E461445, 0x43CB8380, 0x28BA03C8, 0x21A7A2E2, 0x43437ED6, 0x2A9E6670, 0x89B4A106, 0xC6C2F4EE, 0x9C4063CC, 0x2FA0DF6C, 0xB54DC409, 0xCF01538F, 0x616431D7, 0x02CB0E4D, 0x44FFF425, 0xAAD5188E, 0x0742E9BC, 0xFFF41353, 0x130F0A15, 0x787BDC10, 0x4A327B72, 0x702989F7, 0x5F704798, 0x8156A1BB, 0x2BCA3E74, 0x1911A8C4, 0x5E1F27D3, 0x07949DC7, 0xF24C2056, 0xB4299EE6, 0x9C7045D9, 0xA8BF6307, 0x7454AAD2, 0x256425E5, 0xD87DEF67, 0xCFE95452, 0xE7548DF7, 0xA84956C7, 0xD8402C60, 0xCFBD0373, 0x6B6CDAFE}; uint32_t yr_hash(uint32_t seed, const void* buffer, size_t len) { const uint8_t* b = (uint8_t*) buffer; uint32_t result = seed; size_t i; if (len == 0) return result; for (i = len - 1; i > 0; i--) { result ^= ROTATE_INT32(byte_to_int32[*b], i); b++; } result ^= byte_to_int32[*b]; return result; } //////////////////////////////////////////////////////////////////////////////// // Return the value associated to a given key and optionally remove it from // the hash table. Key can be any byte sequence, namespace is a null-terminated // string, and remove is a boolean. // static void* _yr_hash_table_lookup( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns, int remove) { YR_HASH_TABLE_ENTRY* entry; YR_HASH_TABLE_ENTRY* prev_entry; void* result; uint32_t bucket_index = yr_hash(0, key, key_length); if (ns != NULL) bucket_index = yr_hash(bucket_index, (uint8_t*) ns, strlen(ns)); bucket_index = bucket_index % table->size; prev_entry = NULL; entry = table->buckets[bucket_index]; while (entry != NULL) { int key_match = ((entry->key_length == key_length) && (memcmp(entry->key, key, key_length) == 0)); int ns_match = ((entry->ns == ns) || (entry->ns != NULL && ns != NULL && strcmp(entry->ns, ns) == 0)); if (key_match && ns_match) { result = entry->value; if (remove) { if (prev_entry == NULL) table->buckets[bucket_index] = entry->next; else prev_entry->next = entry->next; if (entry->ns != NULL) yr_free(entry->ns); yr_free(entry->key); yr_free(entry); } return result; } prev_entry = entry; entry = entry->next; } return NULL; } YR_API int yr_hash_table_create(int size, YR_HASH_TABLE** table) { YR_HASH_TABLE* new_table; int i; new_table = (YR_HASH_TABLE*) yr_malloc( sizeof(YR_HASH_TABLE) + size * sizeof(YR_HASH_TABLE_ENTRY*)); if (new_table == NULL) return ERROR_INSUFFICIENT_MEMORY; new_table->size = size; for (i = 0; i < size; i++) new_table->buckets[i] = NULL; *table = new_table; return ERROR_SUCCESS; } YR_API void yr_hash_table_clean( YR_HASH_TABLE* table, YR_HASH_TABLE_FREE_VALUE_FUNC free_value) { YR_HASH_TABLE_ENTRY* entry; YR_HASH_TABLE_ENTRY* next_entry; int i; if (table == NULL) return; for (i = 0; i < table->size; i++) { entry = table->buckets[i]; while (entry != NULL) { next_entry = entry->next; if (free_value != NULL) free_value(entry->value); if (entry->ns != NULL) yr_free(entry->ns); yr_free(entry->key); yr_free(entry); entry = next_entry; } table->buckets[i] = NULL; } } YR_API void yr_hash_table_destroy( YR_HASH_TABLE* table, YR_HASH_TABLE_FREE_VALUE_FUNC free_value) { yr_hash_table_clean(table, free_value); yr_free(table); } YR_API void* yr_hash_table_lookup_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns) { return _yr_hash_table_lookup(table, key, key_length, ns, false); } YR_API void* yr_hash_table_remove_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns) { return _yr_hash_table_lookup(table, key, key_length, ns, true); } YR_API int yr_hash_table_add_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns, void* value) { YR_HASH_TABLE_ENTRY* entry; uint32_t bucket_index; entry = (YR_HASH_TABLE_ENTRY*) yr_malloc(sizeof(YR_HASH_TABLE_ENTRY)); if (entry == NULL) return ERROR_INSUFFICIENT_MEMORY; entry->key = yr_malloc(key_length); if (entry->key == NULL) { yr_free(entry); return ERROR_INSUFFICIENT_MEMORY; } if (ns != NULL) { entry->ns = yr_strdup(ns); if (entry->ns == NULL) { yr_free(entry->key); yr_free(entry); return ERROR_INSUFFICIENT_MEMORY; } } else { entry->ns = NULL; } entry->key_length = key_length; entry->value = value; memcpy(entry->key, key, key_length); bucket_index = yr_hash(0, key, key_length); if (ns != NULL) bucket_index = yr_hash(bucket_index, (uint8_t*) ns, strlen(ns)); bucket_index = bucket_index % table->size; entry->next = table->buckets[bucket_index]; table->buckets[bucket_index] = entry; return ERROR_SUCCESS; } YR_API void* yr_hash_table_lookup( YR_HASH_TABLE* table, const char* key, const char* ns) { return yr_hash_table_lookup_raw_key(table, (void*) key, strlen(key), ns); } YR_API void* yr_hash_table_remove( YR_HASH_TABLE* table, const char* key, const char* ns) { return yr_hash_table_remove_raw_key(table, (void*) key, strlen(key), ns); } YR_API int yr_hash_table_add( YR_HASH_TABLE* table, const char* key, const char* ns, void* value) { return yr_hash_table_add_raw_key(table, (void*) key, strlen(key), ns, value); } YR_API int yr_hash_table_add_uint32( YR_HASH_TABLE* table, const char* key, const char* ns, uint32_t value) { return yr_hash_table_add_uint32_raw_key( table, (void*) key, strlen(key), ns, value); } YR_API uint32_t yr_hash_table_lookup_uint32( YR_HASH_TABLE* table, const char* key, const char* ns) { return yr_hash_table_lookup_uint32_raw_key( table, (void*) key, strlen(key), ns); } YR_API int yr_hash_table_add_uint32_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns, uint32_t value) { // Don't allow values equal to UINT32_MAX or UINT32_MAX - 1. if (value >= UINT32_MAX - 1) return ERROR_INVALID_ARGUMENT; // Add +1 to the value in order to avoid putting a NULL pointer in the // hash table once the integer is casted to a pointer. This is undone // by yr_hash_table_lookup_uint32. return yr_hash_table_add_raw_key( table, key, key_length, ns, (void*) (size_t)(value + 1)); } YR_API uint32_t yr_hash_table_lookup_uint32_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns) { void* ptr = yr_hash_table_lookup_raw_key(table, key, key_length, ns); if (ptr == NULL) return UINT32_MAX; // Remove one from the pointer before converting back to integer, see // comment in yr_hash_table_add_uint32. return (uint32_t)(size_t)((uint8_t*) ptr - 1); } yara-4.1.3/libyara/hex_grammar.c000066400000000000000000001471171413423160300165440ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse hex_yyparse #define yylex hex_yylex #define yyerror hex_yyerror #define yydebug hex_yydebug #define yynerrs hex_yynerrs /* Copy the first part of user declarations. */ #line 30 "hex_grammar.y" /* yacc.c:339 */ #include #include #include #include #include #include #include #include #define STR_EXPAND(tok) #tok #define STR(tok) STR_EXPAND(tok) #define YYERROR_VERBOSE #define YYMALLOC yr_malloc #define YYFREE yr_free #define mark_as_not_fast_regexp() \ ((RE_AST *) yyget_extra(yyscanner))->flags &= ~RE_FLAGS_FAST_REGEXP #define fail_if(x, error) \ if (x) \ { \ lex_env->last_error = error; \ YYABORT; \ } #define destroy_node_if(x, node) \ if (x) \ { \ yr_re_node_destroy(node); \ } #line 111 "hex_grammar.c" /* yacc.c:339 */ #ifndef YY_NULLPTR #if defined __cplusplus && 201103L <= __cplusplus #define YY_NULLPTR nullptr #else #define YY_NULLPTR 0 #endif #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE #undef YYERROR_VERBOSE #define YYERROR_VERBOSE 1 #else #define YYERROR_VERBOSE 0 #endif /* In a future release of Bison, this section will be replaced by #include "y.tab.h". */ #ifndef YY_HEX_YY_HEX_GRAMMAR_H_INCLUDED #define YY_HEX_YY_HEX_GRAMMAR_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG #define YYDEBUG 0 #endif #if YYDEBUG extern int hex_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE #define YYTOKENTYPE enum yytokentype { _BYTE_ = 258, _MASKED_BYTE_ = 259, _NUMBER_ = 260 }; #endif /* Tokens. */ #define _BYTE_ 258 #define _MASKED_BYTE_ 259 #define _NUMBER_ 260 /* Value type. */ #if !defined YYSTYPE && !defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 78 "hex_grammar.y" /* yacc.c:355 */ int64_t integer; RE_NODE *re_node; #line 166 "hex_grammar.c" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; #define YYSTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_DECLARED 1 #endif int hex_yyparse(void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env); #endif /* !YY_HEX_YY_HEX_GRAMMAR_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 182 "hex_grammar.c" /* yacc.c:358 */ #ifdef short #undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T #ifdef __SIZE_TYPE__ #define YYSIZE_T __SIZE_TYPE__ #elif defined size_t #define YYSIZE_T size_t #elif !defined YYSIZE_T #include /* INFRINGES ON USER NAME SPACE */ #define YYSIZE_T size_t #else #define YYSIZE_T unsigned int #endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ #if defined YYENABLE_NLS && YYENABLE_NLS #if ENABLE_NLS #include /* INFRINGES ON USER NAME SPACE */ #define YY_(Msgid) dgettext("bison-runtime", Msgid) #endif #endif #ifndef YY_ #define YY_(Msgid) Msgid #endif #endif #ifndef YY_ATTRIBUTE #if ( \ defined __GNUC__ && \ (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) || \ defined __SUNPRO_C && 0x5110 <= __SUNPRO_C #define YY_ATTRIBUTE(Spec) __attribute__(Spec) #else #define YY_ATTRIBUTE(Spec) /* empty */ #endif #endif #ifndef YY_ATTRIBUTE_PURE #define YY_ATTRIBUTE_PURE YY_ATTRIBUTE((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED #define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE((__unused__)) #endif #if !defined _Noreturn && \ (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) #if defined _MSC_VER && 1200 <= _MSC_VER #define _Noreturn __declspec(noreturn) #else #define _Noreturn YY_ATTRIBUTE((__noreturn__)) #endif #endif /* Suppress unused-variable warnings by "using" E. */ #if !defined lint || defined __GNUC__ #define YYUSE(E) ((void) (E)) #else #define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ #define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") #define YY_IGNORE_MAYBE_UNINITIALIZED_END _Pragma("GCC diagnostic pop") #else #define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN #define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN #define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE #define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if !defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ #ifdef YYSTACK_USE_ALLOCA #if YYSTACK_USE_ALLOCA #ifdef __GNUC__ #define YYSTACK_ALLOC __builtin_alloca #elif defined __BUILTIN_VA_ARG_INCR #include /* INFRINGES ON USER NAME SPACE */ #elif defined _AIX #define YYSTACK_ALLOC __alloca #elif defined _MSC_VER #include /* INFRINGES ON USER NAME SPACE */ #define alloca _alloca #else #define YYSTACK_ALLOC alloca #if !defined _ALLOCA_H && !defined EXIT_SUCCESS #include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #endif #endif #endif #endif #ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ #define YYSTACK_FREE(Ptr) \ do \ { /* empty */ \ ; \ } while (0) #ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ #define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ #endif #else #define YYSTACK_ALLOC YYMALLOC #define YYSTACK_FREE YYFREE #ifndef YYSTACK_ALLOC_MAXIMUM #define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM #endif #if ( \ defined __cplusplus && !defined EXIT_SUCCESS && \ !((defined YYMALLOC || defined malloc) && \ (defined YYFREE || defined free))) #include /* INFRINGES ON USER NAME SPACE */ #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #endif #ifndef YYMALLOC #define YYMALLOC malloc #if !defined malloc && !defined EXIT_SUCCESS void *malloc(YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ #endif #endif #ifndef YYFREE #define YYFREE free #if !defined free && !defined EXIT_SUCCESS void free(void *); /* INFRINGES ON USER NAME SPACE */ #endif #endif #endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if ( \ !defined yyoverflow && \ (!defined __cplusplus || \ (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ #define YYSTACK_GAP_MAXIMUM (sizeof(union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ #define YYSTACK_BYTES(N) \ ((N) * (sizeof(yytype_int16) + sizeof(YYSTYPE)) + YYSTACK_GAP_MAXIMUM) #define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ #define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY(&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof(*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof(*yyptr); \ } while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ #ifndef YYCOPY #if defined __GNUC__ && 1 < __GNUC__ #define YYCOPY(Dst, Src, Count) \ __builtin_memcpy(Dst, Src, (Count) * sizeof(*(Src))) #else #define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) (Dst)[yyi] = (Src)[yyi]; \ } while (0) #endif #endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 9 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 30 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 14 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 10 /* YYNRULES -- Number of rules. */ #define YYNRULES 20 /* YYNSTATES -- Number of states. */ #define YYNSTATES 32 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 260 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8, 9, 2, 2, 2, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 13, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5}; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = {0, 105, 105, 114, 118, 130, 141, 150, 159, 163, 172, 177, 176, 189, 225, 257, 279, 299, 303, 320, 329}; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "_BYTE_", "_MASKED_BYTE_", "_NUMBER_", "'{'", "'}'", "'('", "')'", "'['", "']'", "'-'", "'|'", "$accept", "hex_string", "tokens", "token_sequence", "token_or_range", "token", "$@1", "range", "alternatives", "byte", YY_NULLPTR}; #endif #ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = {0, 256, 257, 258, 259, 260, 123, 125, 40, 41, 91, 93, 45, 124}; #endif #define YYPACT_NINF -11 #define yypact_value_is_default(Yystate) (!!((Yystate) == (-11))) #define YYTABLE_NINF -6 #define yytable_value_is_error(Yytable_value) 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { 20, 14, 27, -11, -11, -11, 21, -2, -11, -11, 14, -11, -1, -2, -11, -4, -11, -11, 10, 13, 9, -11, 3, -11, 14, -11, 2, -11, -11, 18, -11, -11}; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = {0, 0, 0, 19, 20, 11, 0, 3, 10, 1, 0, 2, 0, 0, 6, 8, 9, 17, 0, 0, 0, 7, 8, 12, 0, 13, 0, 16, 18, 0, 15, 14}; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = {-11, -11, -10, -11, 17, 8, -11, -11, -11, -11}; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = {-1, 2, 6, 13, 14, 7, 10, 16, 18, 8}; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_int8 yytable[] = { 17, 3, 4, -4, 19, -4, 5, 29, 12, -4, -5, 20, -5, 30, 28, 15, -5, 3, 4, 23, 27, 22, 5, 24, 25, 26, 1, 9, 11, 31, 21}; static const yytype_uint8 yycheck[] = {10, 3, 4, 7, 5, 9, 8, 5, 10, 13, 7, 12, 9, 11, 24, 7, 13, 3, 4, 9, 11, 13, 8, 13, 11, 12, 6, 0, 7, 11, 13}; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 6, 15, 3, 4, 8, 16, 19, 23, 0, 20, 7, 10, 17, 18, 19, 21, 16, 22, 5, 12, 18, 19, 9, 13, 11, 12, 11, 16, 5, 11, 11}; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = {0, 14, 15, 16, 16, 16, 17, 17, 18, 18, 19, 20, 19, 21, 21, 21, 21, 22, 22, 23, 23}; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = {0, 2, 3, 1, 2, 3, 1, 2, 1, 1, 1, 0, 4, 3, 5, 4, 3, 1, 3, 1, 1}; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK(yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror(yyscanner, lex_env, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG #ifndef YYFPRINTF #include /* INFRINGES ON USER NAME SPACE */ #define YYFPRINTF fprintf #endif #define YYDPRINTF(Args) \ do \ { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT #define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif #define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do \ { \ if (yydebug) \ { \ YYFPRINTF(stderr, "%s ", Title); \ yy_symbol_print(stderr, Type, Value, yyscanner, lex_env); \ YYFPRINTF(stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print( FILE *yyoutput, int yytype, YYSTYPE const *const yyvaluep, void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env) { FILE *yyo = yyoutput; YYUSE(yyo); YYUSE(yyscanner); YYUSE(lex_env); if (!yyvaluep) return; #ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT(yyoutput, yytoknum[yytype], *yyvaluep); #endif YYUSE(yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print( FILE *yyoutput, int yytype, YYSTYPE const *const yyvaluep, void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env) { YYFPRINTF( yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print(yyoutput, yytype, yyvaluep, yyscanner, lex_env); YYFPRINTF(yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print(yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF(stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF(stderr, " %d", yybot); } YYFPRINTF(stderr, "\n"); } #define YY_STACK_PRINT(Bottom, Top) \ do \ { \ if (yydebug) \ yy_stack_print((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print( yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF( stderr, "Reducing stack by rule %d (line %" PRIu64 "):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF(stderr, " $%d = ", yyi + 1); yy_symbol_print( stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]), yyscanner, lex_env); YYFPRINTF(stderr, "\n"); } } #define YY_REDUCE_PRINT(Rule) \ do \ { \ if (yydebug) \ yy_reduce_print(yyssp, yyvsp, Rule, yyscanner, lex_env); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ #define YYDPRINTF(Args) #define YY_SYMBOL_PRINT(Title, Type, Value, Location) #define YY_STACK_PRINT(Bottom, Top) #define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH #define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH #define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE #ifndef yystrlen #if defined __GLIBC__ && defined _STRING_H #define yystrlen strlen #else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen(const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } #endif #endif #ifndef yystpcpy #if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE #define yystpcpy stpcpy #else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char *yystpcpy(char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } #endif #endif #ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr(char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes:; } if (!yyres) return yystrlen(yystr); return yystpcpy(yyres, yystr) - yyres; } #endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error( YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr(YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default(yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error(yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr(YY_NULLPTR, yytname[yyx]); if (!(yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { #define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_( 5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); #undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen(yyformat); if (!(yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (!(yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr(yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct( const char *yymsg, int yytype, YYSTYPE *yyvaluep, void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env) { YYUSE(yyvaluep); YYUSE(yyscanner); YYUSE(lex_env); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT(yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { case 16: /* tokens */ #line 94 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1024 "hex_grammar.c" /* yacc.c:1258 */ break; case 17: /* token_sequence */ #line 95 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1030 "hex_grammar.c" /* yacc.c:1258 */ break; case 18: /* token_or_range */ #line 96 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1036 "hex_grammar.c" /* yacc.c:1258 */ break; case 19: /* token */ #line 97 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1042 "hex_grammar.c" /* yacc.c:1258 */ break; case 21: /* range */ #line 100 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1048 "hex_grammar.c" /* yacc.c:1258 */ break; case 22: /* alternatives */ #line 99 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1054 "hex_grammar.c" /* yacc.c:1258 */ break; case 23: /* byte */ #line 98 "hex_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1060 "hex_grammar.c" /* yacc.c:1258 */ break; default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse(void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env) { /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ YY_INITIAL_VALUE(static YYSTYPE yyval_default;) YYSTYPE yylval YY_INITIAL_VALUE(= yyval_default); /* Number of syntax errors so far. */ int yynerrs; int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow( YY_("memory exhausted"), &yyss1, yysize * sizeof(*yyssp), &yyvs1, yysize * sizeof(*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ #ifndef YYSTACK_RELOCATE goto yyexhaustedlab; #else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC( YYSTACK_BYTES(yystacksize)); if (!yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE(yyss_alloc, yyss); YYSTACK_RELOCATE(yyvs_alloc, yyvs); #undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE(yyss1); } #endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF( (stderr, "Stack size increased to %" PRIu64 "\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default(yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF((stderr, "Reading a token: ")); yychar = yylex(&yylval, yyscanner, lex_env); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE(yychar); YY_SYMBOL_PRINT("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error(yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1 - yylen]; YY_REDUCE_PRINT(yyn); switch (yyn) { case 2: #line 106 "hex_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast = yyget_extra(yyscanner); re_ast->root_node = (yyvsp[-1].re_node); } #line 1331 "hex_grammar.c" /* yacc.c:1663 */ break; case 3: #line 115 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); } #line 1339 "hex_grammar.c" /* yacc.c:1663 */ break; case 4: #line 119 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_CONCAT); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-1].re_node)); destroy_node_if((yyval.re_node) == NULL, (yyvsp[0].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-1].re_node)); yr_re_node_append_child((yyval.re_node), (yyvsp[0].re_node)); } #line 1355 "hex_grammar.c" /* yacc.c:1663 */ break; case 5: #line 131 "hex_grammar.y" /* yacc.c:1663 */ { yr_re_node_append_child((yyvsp[-1].re_node), (yyvsp[0].re_node)); yr_re_node_prepend_child((yyvsp[-1].re_node), (yyvsp[-2].re_node)); (yyval.re_node) = (yyvsp[-1].re_node); } #line 1366 "hex_grammar.c" /* yacc.c:1663 */ break; case 6: #line 142 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_CONCAT); destroy_node_if((yyval.re_node) == NULL, (yyvsp[0].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[0].re_node)); } #line 1379 "hex_grammar.c" /* yacc.c:1663 */ break; case 7: #line 151 "hex_grammar.y" /* yacc.c:1663 */ { yr_re_node_append_child((yyvsp[-1].re_node), (yyvsp[0].re_node)); (yyval.re_node) = (yyvsp[-1].re_node); } #line 1388 "hex_grammar.c" /* yacc.c:1663 */ break; case 8: #line 160 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); } #line 1396 "hex_grammar.c" /* yacc.c:1663 */ break; case 9: #line 164 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); (yyval.re_node)->greedy = false; } #line 1405 "hex_grammar.c" /* yacc.c:1663 */ break; case 10: #line 173 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); } #line 1413 "hex_grammar.c" /* yacc.c:1663 */ break; case 11: #line 177 "hex_grammar.y" /* yacc.c:1663 */ { lex_env->inside_or++; } #line 1421 "hex_grammar.c" /* yacc.c:1663 */ break; case 12: #line 181 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[-1].re_node); lex_env->inside_or--; } #line 1430 "hex_grammar.c" /* yacc.c:1663 */ break; case 13: #line 190 "hex_grammar.y" /* yacc.c:1663 */ { if ((yyvsp[-1].integer) <= 0) { yyerror(yyscanner, lex_env, "invalid jump length"); YYABORT; } if (lex_env->inside_or && (yyvsp[-1].integer) > YR_STRING_CHAINING_THRESHOLD) { yyerror( yyscanner, lex_env, "jumps over " STR(YR_STRING_CHAINING_THRESHOLD) " not allowed inside " "alternation (|)"); YYABORT; } // A jump of one is equivalent to ?? if ((yyvsp[-1].integer) == 1) { (yyval.re_node) = yr_re_node_create(RE_NODE_MASKED_LITERAL); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->value = 0x00; (yyval.re_node)->mask = 0x00; } else { (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->start = (int) (yyvsp[-1].integer); (yyval.re_node)->end = (int) (yyvsp[-1].integer); } } #line 1470 "hex_grammar.c" /* yacc.c:1663 */ break; case 14: #line 226 "hex_grammar.y" /* yacc.c:1663 */ { if (lex_env->inside_or && ((yyvsp[-3].integer) > YR_STRING_CHAINING_THRESHOLD || (yyvsp[-1].integer) > YR_STRING_CHAINING_THRESHOLD)) { yyerror( yyscanner, lex_env, "jumps over " STR(YR_STRING_CHAINING_THRESHOLD) " not allowed inside " "alternation (|)"); YYABORT; } if ((yyvsp[-3].integer) < 0 || (yyvsp[-1].integer) < 0) { yyerror(yyscanner, lex_env, "invalid negative jump length"); YYABORT; } if ((yyvsp[-3].integer) > (yyvsp[-1].integer)) { yyerror(yyscanner, lex_env, "invalid jump range"); YYABORT; } (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->start = (int) (yyvsp[-3].integer); (yyval.re_node)->end = (int) (yyvsp[-1].integer); } #line 1506 "hex_grammar.c" /* yacc.c:1663 */ break; case 15: #line 258 "hex_grammar.y" /* yacc.c:1663 */ { if (lex_env->inside_or) { yyerror( yyscanner, lex_env, "unbounded jumps not allowed inside alternation (|)"); YYABORT; } if ((yyvsp[-2].integer) < 0) { yyerror(yyscanner, lex_env, "invalid negative jump length"); YYABORT; } (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->start = (int) (yyvsp[-2].integer); (yyval.re_node)->end = INT_MAX; } #line 1532 "hex_grammar.c" /* yacc.c:1663 */ break; case 16: #line 280 "hex_grammar.y" /* yacc.c:1663 */ { if (lex_env->inside_or) { yyerror( yyscanner, lex_env, "unbounded jumps not allowed inside alternation (|)"); YYABORT; } (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->start = 0; (yyval.re_node)->end = INT_MAX; } #line 1552 "hex_grammar.c" /* yacc.c:1663 */ break; case 17: #line 300 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); } #line 1560 "hex_grammar.c" /* yacc.c:1663 */ break; case 18: #line 304 "hex_grammar.y" /* yacc.c:1663 */ { mark_as_not_fast_regexp(); (yyval.re_node) = yr_re_node_create(RE_NODE_ALT); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-2].re_node)); destroy_node_if((yyval.re_node) == NULL, (yyvsp[0].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-2].re_node)); yr_re_node_append_child((yyval.re_node), (yyvsp[0].re_node)); } #line 1578 "hex_grammar.c" /* yacc.c:1663 */ break; case 19: #line 321 "hex_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_LITERAL); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->value = (int) (yyvsp[0].integer); (yyval.re_node)->mask = 0xFF; } #line 1591 "hex_grammar.c" /* yacc.c:1663 */ break; case 20: #line 330 "hex_grammar.y" /* yacc.c:1663 */ { uint8_t mask = (uint8_t)((yyvsp[0].integer) >> 8); if (mask == 0x00) { (yyval.re_node) = yr_re_node_create(RE_NODE_ANY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->value = 0x00; (yyval.re_node)->mask = 0x00; } else { (yyval.re_node) = yr_re_node_create(RE_NODE_MASKED_LITERAL); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->value = (yyvsp[0].integer) & 0xFF; (yyval.re_node)->mask = mask; } } #line 1618 "hex_grammar.c" /* yacc.c:1663 */ break; #line 1622 "hex_grammar.c" /* yacc.c:1663 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK(yylen); yylen = 0; YY_STACK_PRINT(yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE(yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if !YYERROR_VERBOSE yyerror(yyscanner, lex_env, YY_("syntax error")); #else #define YYSYNTAX_ERROR yysyntax_error(&yymsg_alloc, &yymsg, yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE(yymsg); yymsg = (char *) YYSTACK_ALLOC(yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror(yyscanner, lex_env, yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } #undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct("Error: discarding", yytoken, &yylval, yyscanner, lex_env); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK(yylen); yylen = 0; YY_STACK_PRINT(yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default(yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct("Error: popping", yystos[yystate], yyvsp, yyscanner, lex_env); YYPOPSTACK(1); yystate = *yyssp; YY_STACK_PRINT(yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror(yyscanner, lex_env, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE(yychar); yydestruct( "Cleanup: discarding lookahead", yytoken, &yylval, yyscanner, lex_env); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK(yylen); YY_STACK_PRINT(yyss, yyssp); while (yyssp != yyss) { yydestruct("Cleanup: popping", yystos[*yyssp], yyvsp, yyscanner, lex_env); YYPOPSTACK(1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE(yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE(yymsg); #endif return yyresult; } #line 354 "hex_grammar.y" /* yacc.c:1907 */ yara-4.1.3/libyara/hex_grammar.h000066400000000000000000000045151413423160300165430ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ #ifndef YY_HEX_YY_HEX_GRAMMAR_H_INCLUDED #define YY_HEX_YY_HEX_GRAMMAR_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG #define YYDEBUG 0 #endif #if YYDEBUG extern int hex_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE #define YYTOKENTYPE enum yytokentype { _BYTE_ = 258, _MASKED_BYTE_ = 259, _NUMBER_ = 260 }; #endif /* Tokens. */ #define _BYTE_ 258 #define _MASKED_BYTE_ 259 #define _NUMBER_ 260 /* Value type. */ #if !defined YYSTYPE && !defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 78 "hex_grammar.y" /* yacc.c:1916 */ int64_t integer; RE_NODE *re_node; #line 69 "hex_grammar.h" /* yacc.c:1916 */ }; typedef union YYSTYPE YYSTYPE; #define YYSTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_DECLARED 1 #endif int hex_yyparse(void *yyscanner, HEX_LEX_ENVIRONMENT *lex_env); #endif /* !YY_HEX_YY_HEX_GRAMMAR_H_INCLUDED */ yara-4.1.3/libyara/hex_grammar.y000066400000000000000000000177261413423160300165740ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ %{ #include #include #include #include #include #include #include #include #define STR_EXPAND(tok) #tok #define STR(tok) STR_EXPAND(tok) #define YYERROR_VERBOSE #define YYMALLOC yr_malloc #define YYFREE yr_free #define mark_as_not_fast_regexp() \ ((RE_AST*) yyget_extra(yyscanner))->flags &= ~RE_FLAGS_FAST_REGEXP #define fail_if(x, error) \ if (x) \ { \ lex_env->last_error = error; \ YYABORT; \ } \ #define destroy_node_if(x, node) \ if (x) \ { \ yr_re_node_destroy(node); \ } \ %} %name-prefix "hex_yy" %pure-parser %parse-param {void *yyscanner} %parse-param {HEX_LEX_ENVIRONMENT *lex_env} %lex-param {yyscan_t yyscanner} %lex-param {HEX_LEX_ENVIRONMENT *lex_env} %union { int64_t integer; RE_NODE *re_node; } %token _BYTE_ %token _MASKED_BYTE_ %token _NUMBER_ %type tokens %type token_sequence %type token_or_range %type token byte %type alternatives %type range %destructor { yr_re_node_destroy($$); $$ = NULL; } tokens %destructor { yr_re_node_destroy($$); $$ = NULL; } token_sequence %destructor { yr_re_node_destroy($$); $$ = NULL; } token_or_range %destructor { yr_re_node_destroy($$); $$ = NULL; } token %destructor { yr_re_node_destroy($$); $$ = NULL; } byte %destructor { yr_re_node_destroy($$); $$ = NULL; } alternatives %destructor { yr_re_node_destroy($$); $$ = NULL; } range %% hex_string : '{' tokens '}' { RE_AST* re_ast = yyget_extra(yyscanner); re_ast->root_node = $2; } ; tokens : token { $$ = $1; } | token token { $$ = yr_re_node_create(RE_NODE_CONCAT); destroy_node_if($$ == NULL, $1); destroy_node_if($$ == NULL, $2); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); yr_re_node_append_child($$, $2); } | token token_sequence token { yr_re_node_append_child($2, $3); yr_re_node_prepend_child($2, $1); $$ = $2; } ; token_sequence : token_or_range { $$ = yr_re_node_create(RE_NODE_CONCAT); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } | token_sequence token_or_range { yr_re_node_append_child($1, $2); $$ = $1; } ; token_or_range : token { $$ = $1; } | range { $$ = $1; $$->greedy = false; } ; token : byte { $$ = $1; } | '(' { lex_env->inside_or++; } alternatives ')' { $$ = $3; lex_env->inside_or--; } ; range : '[' _NUMBER_ ']' { if ($2 <= 0) { yyerror(yyscanner, lex_env, "invalid jump length"); YYABORT; } if (lex_env->inside_or && $2 > YR_STRING_CHAINING_THRESHOLD) { yyerror(yyscanner, lex_env, "jumps over " STR(YR_STRING_CHAINING_THRESHOLD) " not allowed inside alternation (|)"); YYABORT; } // A jump of one is equivalent to ?? if ($2 == 1) { $$ = yr_re_node_create(RE_NODE_MASKED_LITERAL); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->value = 0x00; $$->mask = 0x00; } else { $$ = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->start = (int) $2; $$->end = (int) $2; } } | '[' _NUMBER_ '-' _NUMBER_ ']' { if (lex_env->inside_or && ($2 > YR_STRING_CHAINING_THRESHOLD || $4 > YR_STRING_CHAINING_THRESHOLD) ) { yyerror(yyscanner, lex_env, "jumps over " STR(YR_STRING_CHAINING_THRESHOLD) " not allowed inside alternation (|)"); YYABORT; } if ($2 < 0 || $4 < 0) { yyerror(yyscanner, lex_env, "invalid negative jump length"); YYABORT; } if ($2 > $4) { yyerror(yyscanner, lex_env, "invalid jump range"); YYABORT; } $$ = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->start = (int) $2; $$->end = (int) $4; } | '[' _NUMBER_ '-' ']' { if (lex_env->inside_or) { yyerror(yyscanner, lex_env, "unbounded jumps not allowed inside alternation (|)"); YYABORT; } if ($2 < 0) { yyerror(yyscanner, lex_env, "invalid negative jump length"); YYABORT; } $$ = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->start = (int) $2; $$->end = INT_MAX; } | '[' '-' ']' { if (lex_env->inside_or) { yyerror(yyscanner, lex_env, "unbounded jumps not allowed inside alternation (|)"); YYABORT; } $$ = yr_re_node_create(RE_NODE_RANGE_ANY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->start = 0; $$->end = INT_MAX; } ; alternatives : tokens { $$ = $1; } | alternatives '|' tokens { mark_as_not_fast_regexp(); $$ = yr_re_node_create(RE_NODE_ALT); destroy_node_if($$ == NULL, $1); destroy_node_if($$ == NULL, $3); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); yr_re_node_append_child($$, $3); } ; byte : _BYTE_ { $$ = yr_re_node_create(RE_NODE_LITERAL); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->value = (int) $1; $$->mask = 0xFF; } | _MASKED_BYTE_ { uint8_t mask = (uint8_t) ($1 >> 8); if (mask == 0x00) { $$ = yr_re_node_create(RE_NODE_ANY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->value = 0x00; $$->mask = 0x00; } else { $$ = yr_re_node_create(RE_NODE_MASKED_LITERAL); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->value = $1 & 0xFF; $$->mask = mask; } } ; %%yara-4.1.3/libyara/hex_lexer.c000066400000000000000000002020441413423160300162240ustar00rootroot00000000000000#line 1 "hex_lexer.c" #line 3 "hex_lexer.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif #ifdef yy_create_buffer #define hex_yy_create_buffer_ALREADY_DEFINED #else #define yy_create_buffer hex_yy_create_buffer #endif #ifdef yy_delete_buffer #define hex_yy_delete_buffer_ALREADY_DEFINED #else #define yy_delete_buffer hex_yy_delete_buffer #endif #ifdef yy_scan_buffer #define hex_yy_scan_buffer_ALREADY_DEFINED #else #define yy_scan_buffer hex_yy_scan_buffer #endif #ifdef yy_scan_string #define hex_yy_scan_string_ALREADY_DEFINED #else #define yy_scan_string hex_yy_scan_string #endif #ifdef yy_scan_bytes #define hex_yy_scan_bytes_ALREADY_DEFINED #else #define yy_scan_bytes hex_yy_scan_bytes #endif #ifdef yy_init_buffer #define hex_yy_init_buffer_ALREADY_DEFINED #else #define yy_init_buffer hex_yy_init_buffer #endif #ifdef yy_flush_buffer #define hex_yy_flush_buffer_ALREADY_DEFINED #else #define yy_flush_buffer hex_yy_flush_buffer #endif #ifdef yy_load_buffer_state #define hex_yy_load_buffer_state_ALREADY_DEFINED #else #define yy_load_buffer_state hex_yy_load_buffer_state #endif #ifdef yy_switch_to_buffer #define hex_yy_switch_to_buffer_ALREADY_DEFINED #else #define yy_switch_to_buffer hex_yy_switch_to_buffer #endif #ifdef yypush_buffer_state #define hex_yypush_buffer_state_ALREADY_DEFINED #else #define yypush_buffer_state hex_yypush_buffer_state #endif #ifdef yypop_buffer_state #define hex_yypop_buffer_state_ALREADY_DEFINED #else #define yypop_buffer_state hex_yypop_buffer_state #endif #ifdef yyensure_buffer_stack #define hex_yyensure_buffer_stack_ALREADY_DEFINED #else #define yyensure_buffer_stack hex_yyensure_buffer_stack #endif #ifdef yylex #define hex_yylex_ALREADY_DEFINED #else #define yylex hex_yylex #endif #ifdef yyrestart #define hex_yyrestart_ALREADY_DEFINED #else #define yyrestart hex_yyrestart #endif #ifdef yylex_init #define hex_yylex_init_ALREADY_DEFINED #else #define yylex_init hex_yylex_init #endif #ifdef yylex_init_extra #define hex_yylex_init_extra_ALREADY_DEFINED #else #define yylex_init_extra hex_yylex_init_extra #endif #ifdef yylex_destroy #define hex_yylex_destroy_ALREADY_DEFINED #else #define yylex_destroy hex_yylex_destroy #endif #ifdef yyget_debug #define hex_yyget_debug_ALREADY_DEFINED #else #define yyget_debug hex_yyget_debug #endif #ifdef yyset_debug #define hex_yyset_debug_ALREADY_DEFINED #else #define yyset_debug hex_yyset_debug #endif #ifdef yyget_extra #define hex_yyget_extra_ALREADY_DEFINED #else #define yyget_extra hex_yyget_extra #endif #ifdef yyset_extra #define hex_yyset_extra_ALREADY_DEFINED #else #define yyset_extra hex_yyset_extra #endif #ifdef yyget_in #define hex_yyget_in_ALREADY_DEFINED #else #define yyget_in hex_yyget_in #endif #ifdef yyset_in #define hex_yyset_in_ALREADY_DEFINED #else #define yyset_in hex_yyset_in #endif #ifdef yyget_out #define hex_yyget_out_ALREADY_DEFINED #else #define yyget_out hex_yyget_out #endif #ifdef yyset_out #define hex_yyset_out_ALREADY_DEFINED #else #define yyset_out hex_yyset_out #endif #ifdef yyget_leng #define hex_yyget_leng_ALREADY_DEFINED #else #define yyget_leng hex_yyget_leng #endif #ifdef yyget_text #define hex_yyget_text_ALREADY_DEFINED #else #define yyget_text hex_yyget_text #endif #ifdef yyget_lineno #define hex_yyget_lineno_ALREADY_DEFINED #else #define yyget_lineno hex_yyget_lineno #endif #ifdef yyset_lineno #define hex_yyset_lineno_ALREADY_DEFINED #else #define yyset_lineno hex_yyset_lineno #endif #ifdef yyget_column #define hex_yyget_column_ALREADY_DEFINED #else #define yyget_column hex_yyget_column #endif #ifdef yyset_column #define hex_yyset_column_ALREADY_DEFINED #else #define yyset_column hex_yyset_column #endif #ifdef yywrap #define hex_yywrap_ALREADY_DEFINED #else #define yywrap hex_yywrap #endif #ifdef yyget_lval #define hex_yyget_lval_ALREADY_DEFINED #else #define yyget_lval hex_yyget_lval #endif #ifdef yyset_lval #define hex_yyset_lval_ALREADY_DEFINED #else #define yyset_lval hex_yyset_lval #endif #ifdef yyalloc #define hex_yyalloc_ALREADY_DEFINED #else #define yyalloc hex_yyalloc #endif #ifdef yyrealloc #define hex_yyrealloc_ALREADY_DEFINED #else #define yyrealloc hex_yyrealloc #endif #ifdef yyfree #define hex_yyfree_ALREADY_DEFINED #else #define yyfree hex_yyfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an * integer in range [0..255] for use as an array index. */ #define YY_SC_TO_UI(c) ((YY_CHAR) (c)) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) #define YY_LINENO_REWIND_TO(dst) \ do {\ const char *p;\ for ( p = yy_cp-1; p >= (dst); --p)\ if ( *p == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void yyrestart ( FILE *input_file , yyscan_t yyscanner ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); void yypop_buffer_state ( yyscan_t yyscanner ); static void yyensure_buffer_stack ( yyscan_t yyscanner ); static void yy_load_buffer_state ( yyscan_t yyscanner ); static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); #define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); void yyfree ( void * , yyscan_t yyscanner ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define hex_yywrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); static int yy_get_next_buffer ( yyscan_t yyscanner ); static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 19 #define YY_END_OF_BUFFER 20 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static const flex_int16_t yy_accept[34] = { 0, 0, 0, 0, 0, 0, 0, 20, 18, 16, 16, 17, 18, 5, 18, 6, 9, 9, 15, 14, 14, 11, 12, 13, 7, 10, 1, 2, 3, 4, 8, 12, 10, 0 } ; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 4, 4, 5, 1, 1, 6, 1, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 9, 1, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 11, 1, 12, 1, 1, 1, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const YY_CHAR yy_meta[13] = { 0, 1, 1, 2, 1, 1, 1, 1, 3, 3, 3, 1, 1 } ; static const flex_int16_t yy_base[38] = { 0, 0, 0, 36, 35, 12, 0, 39, 42, 42, 42, 42, 20, 29, 28, 42, 42, 29, 42, 42, 42, 42, 27, 42, 42, 0, 42, 42, 42, 42, 42, 26, 0, 42, 27, 29, 23, 30 } ; static const flex_int16_t yy_def[38] = { 0, 33, 1, 34, 34, 33, 5, 33, 33, 33, 33, 33, 33, 35, 36, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 37, 33, 33, 33, 33, 33, 33, 37, 0, 33, 33, 33, 33 } ; static const flex_int16_t yy_nxt[55] = { 0, 8, 9, 10, 11, 8, 8, 12, 13, 14, 13, 15, 8, 18, 19, 20, 18, 18, 21, 18, 22, 18, 18, 18, 23, 24, 28, 25, 16, 16, 16, 32, 26, 32, 31, 31, 30, 29, 27, 33, 17, 17, 7, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 } ; static const flex_int16_t yy_chk[55] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 12, 36, 12, 34, 34, 34, 37, 35, 37, 31, 22, 17, 14, 13, 7, 4, 3, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33 } ; /* Table of booleans, true if rule could match eol. */ static const flex_int32_t yy_rule_can_match_eol[20] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, }; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "hex_lexer.l" /* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Lexical analyzer for hex strings */ #line 33 "hex_lexer.l" /* Disable warnings for unused functions in this file. As we redefine YY_FATAL_ERROR macro to use our own function hex_yyfatal, the yy_fatal_error function generated by Flex is not actually used, causing a compiler warning. Flex doesn't offer any options to remove the yy_fatal_error function. When they include something like %option noyy_fatal_error as they do with noyywrap then we can remove this pragma. */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #include #include #include #include #include #include #include "hex_grammar.h" #ifdef _WIN32 #define snprintf _snprintf #endif #define ERROR_IF(x, error) \ if (x) \ { \ RE_AST* re_ast = yyget_extra(yyscanner); \ re_ast->error_code = error; \ YYABORT; \ } \ #line 757 "hex_lexer.c" #define YY_NO_UNISTD_H 1 #define YY_NO_INPUT 1 #line 761 "hex_lexer.c" #define INITIAL 0 #define comment 1 #define range 2 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; }; /* end struct yyguts_t */ static int yy_init_globals ( yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r int yylex_init (yyscan_t* scanner); int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy ( yyscan_t yyscanner ); int yyget_debug ( yyscan_t yyscanner ); void yyset_debug ( int debug_flag , yyscan_t yyscanner ); YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); FILE *yyget_in ( yyscan_t yyscanner ); void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); int yyget_lineno ( yyscan_t yyscanner ); void yyset_lineno ( int _line_number , yyscan_t yyscanner ); int yyget_column ( yyscan_t yyscanner ); void yyset_column ( int _column_no , yyscan_t yyscanner ); YYSTYPE * yyget_lval ( yyscan_t yyscanner ); void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap ( yyscan_t yyscanner ); #else extern int yywrap ( yyscan_t yyscanner ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput ( yyscan_t yyscanner ); #else static int input ( yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner); #define YY_DECL int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_load_buffer_state( yyscanner ); } { #line 95 "hex_lexer.l" #line 1039 "hex_lexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 34 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } while ( yy_current_state != 33 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; for ( yyl = 0; yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 98 "hex_lexer.l" { yylval->integer = xtoi(yytext); return _BYTE_; } YY_BREAK case 2: YY_RULE_SETUP #line 104 "hex_lexer.l" { yytext[1] = '0'; // replace ? by 0 yylval->integer = xtoi(yytext) | 0xF000 ; return _MASKED_BYTE_; } YY_BREAK case 3: YY_RULE_SETUP #line 111 "hex_lexer.l" { yytext[0] = '0'; // replace ? by 0 yylval->integer = xtoi(yytext) | 0x0F00 ; return _MASKED_BYTE_; } YY_BREAK case 4: YY_RULE_SETUP #line 118 "hex_lexer.l" { yylval->integer = 0x0000; return _MASKED_BYTE_; } YY_BREAK case 5: YY_RULE_SETUP #line 124 "hex_lexer.l" { yyerror(yyscanner, lex_env, "uneven number of digits in hex string"); yyterminate(); } YY_BREAK case 6: YY_RULE_SETUP #line 130 "hex_lexer.l" { BEGIN(range); return yytext[0]; } YY_BREAK case 7: YY_RULE_SETUP #line 136 "hex_lexer.l" { BEGIN(comment); } YY_BREAK case 8: YY_RULE_SETUP #line 141 "hex_lexer.l" { BEGIN(INITIAL); } YY_BREAK case 9: /* rule 9 can match eol */ YY_RULE_SETUP #line 146 "hex_lexer.l" // skip comments YY_BREAK case 10: YY_RULE_SETUP #line 148 "hex_lexer.l" // skip single-line comments YY_BREAK case 11: YY_RULE_SETUP #line 150 "hex_lexer.l" { return yytext[0]; } YY_BREAK case 12: YY_RULE_SETUP #line 155 "hex_lexer.l" { yylval->integer = atoi(yytext); return _NUMBER_; } YY_BREAK case 13: YY_RULE_SETUP #line 161 "hex_lexer.l" { BEGIN(INITIAL); return yytext[0]; } YY_BREAK case 14: /* rule 14 can match eol */ YY_RULE_SETUP #line 167 "hex_lexer.l" // skip whitespaces YY_BREAK case 15: YY_RULE_SETUP #line 169 "hex_lexer.l" { yyerror(yyscanner, lex_env, "invalid character in hex string jump"); yyterminate(); } YY_BREAK case 16: /* rule 16 can match eol */ YY_RULE_SETUP #line 175 "hex_lexer.l" // skip whitespaces YY_BREAK case 17: YY_RULE_SETUP #line 177 "hex_lexer.l" { // pass valid characters to the parser return yytext[0]; } YY_BREAK case 18: YY_RULE_SETUP #line 182 "hex_lexer.l" { // reject all other characters yyerror(yyscanner, lex_env, "invalid character in hex string"); yyterminate(); } YY_BREAK case 19: YY_RULE_SETUP #line 188 "hex_lexer.l" ECHO; YY_BREAK #line 1256 "hex_lexer.c" case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(comment): case YY_STATE_EOF(range): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc( (void *) b->yy_ch_buf, (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); /* "- 2" to take care of EOB's */ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { yy_state_type yy_current_state; char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 34 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ char *yy_cp = yyg->yy_c_buf_p; YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 34 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; yy_is_jam = (yy_current_state == 33); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; if ( c == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b , yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param _line_number line number * @param yyscanner The scanner object. */ void yyset_lineno (int _line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column number * @param yyscanner The scanner object. */ void yyset_column (int _column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_column called with no buffer" ); yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ void yyset_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = _in_str ; } void yyset_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = _out_str ; } int yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } /* User-visible API */ /* yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* yylex_init_extra has the same functionality as yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to yyalloc in * the yyextra field. */ int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = NULL; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ yyfree(yyg->yy_buffer_stack , yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ yyfree( yyg->yy_start_stack , yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(size); } void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 188 "hex_lexer.l" // // yyfatal (actually named hex_yyfatal because of the '%option prefix="hex_yy"' // directive) is called when a fatal error occurs in the parser. When this // happens we are deep inside the parsing logic generated by flex/bison and // the only way to exit gracefully from there is using setjmp/longjmp. // void yyfatal( yyscan_t yyscanner, const char *error_message) { jmp_buf* recovery_trampoline = (jmp_buf*) yr_thread_storage_get_value( &yr_yyfatal_trampoline_tls); // Never returns. longjmp(*recovery_trampoline, 1); } void yyerror( yyscan_t yyscanner, HEX_LEX_ENVIRONMENT* lex_env, const char *error_message) { // if lex_env->last_error was set to some error code before // don't overwrite it, we are interested in the first error, not in // subsequent errors like "syntax error, unexpected $end" caused by // early parser termination. if (lex_env->last_error == ERROR_SUCCESS) { lex_env->last_error = ERROR_INVALID_HEX_STRING; strlcpy( lex_env->last_error_message, error_message, sizeof(lex_env->last_error_message)); } } int yr_parse_hex_string( const char* hex_string, RE_AST** re_ast, RE_ERROR* error) { yyscan_t yyscanner; jmp_buf recovery_trampoline; HEX_LEX_ENVIRONMENT lex_env; lex_env.last_error = ERROR_SUCCESS; lex_env.inside_or = 0; yr_thread_storage_set_value( &yr_yyfatal_trampoline_tls, &recovery_trampoline); if (setjmp(recovery_trampoline) != 0) return ERROR_INTERNAL_FATAL_ERROR; FAIL_ON_ERROR(yr_re_ast_create(re_ast)); // The RE_FLAGS_FAST_REGEXP flag indicates a regular expression can be // matched by faster algorithm. These regular expressions come from hex // strings that do not contain alternatives, like in: // // { ( 01 02 | 03 04) 05 06 }. // // This flag is unset later during parsing if alternatives are used. (*re_ast)->flags |= RE_FLAGS_FAST_REGEXP; // Set RE_FLAGS_DOT_ALL because in hex strings the "dot" (?? in this case) // must match all characters including new-line. (*re_ast)->flags |= RE_FLAGS_DOT_ALL; yylex_init(&yyscanner); yyset_extra(*re_ast, yyscanner); yy_scan_string(hex_string, yyscanner); yyparse(yyscanner, &lex_env); yylex_destroy(yyscanner); if (lex_env.last_error != ERROR_SUCCESS) { strlcpy(error->message, lex_env.last_error_message, sizeof(error->message)); return lex_env.last_error; } return ERROR_SUCCESS; } yara-4.1.3/libyara/hex_lexer.l000066400000000000000000000146241413423160300162420ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Lexical analyzer for hex strings */ %{ /* Disable warnings for unused functions in this file. As we redefine YY_FATAL_ERROR macro to use our own function hex_yyfatal, the yy_fatal_error function generated by Flex is not actually used, causing a compiler warning. Flex doesn't offer any options to remove the yy_fatal_error function. When they include something like %option noyy_fatal_error as they do with noyywrap then we can remove this pragma. */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #include #include #include #include #include #include #include "hex_grammar.h" #ifdef _WIN32 #define snprintf _snprintf #endif #define ERROR_IF(x, error) \ if (x) \ { \ RE_AST* re_ast = yyget_extra(yyscanner); \ re_ast->error_code = error; \ YYABORT; \ } \ %} %option reentrant bison-bridge %option noyywrap %option nounistd %option noinput %option nounput %option never-interactive %option yylineno %option prefix="hex_yy" %option outfile="lex.yy.c" %option verbose %option warn digit [0-9] letter [a-zA-Z] hexdigit [a-fA-F0-9] %x comment %x range %% {hexdigit}{2} { yylval->integer = xtoi(yytext); return _BYTE_; } {hexdigit}\? { yytext[1] = '0'; // replace ? by 0 yylval->integer = xtoi(yytext) | 0xF000 ; return _MASKED_BYTE_; } \?{hexdigit} { yytext[0] = '0'; // replace ? by 0 yylval->integer = xtoi(yytext) | 0x0F00 ; return _MASKED_BYTE_; } \?\? { yylval->integer = 0x0000; return _MASKED_BYTE_; } {hexdigit} { yyerror(yyscanner, lex_env, "uneven number of digits in hex string"); yyterminate(); } \[ { BEGIN(range); return yytext[0]; } "/*" { BEGIN(comment); } "*/" { BEGIN(INITIAL); } .|\n // skip comments "//".* // skip single-line comments \- { return yytext[0]; } {digit}+ { yylval->integer = atoi(yytext); return _NUMBER_; } \] { BEGIN(INITIAL); return yytext[0]; } [ \t\r\n] // skip whitespaces . { yyerror(yyscanner, lex_env, "invalid character in hex string jump"); yyterminate(); } [ \t\r\n] // skip whitespaces [{}()|] { // pass valid characters to the parser return yytext[0]; } . { // reject all other characters yyerror(yyscanner, lex_env, "invalid character in hex string"); yyterminate(); } %% // // yyfatal (actually named hex_yyfatal because of the '%option prefix="hex_yy"' // directive) is called when a fatal error occurs in the parser. When this // happens we are deep inside the parsing logic generated by flex/bison and // the only way to exit gracefully from there is using setjmp/longjmp. // void yyfatal( yyscan_t yyscanner, const char *error_message) { jmp_buf* recovery_trampoline = (jmp_buf*) yr_thread_storage_get_value( &yr_yyfatal_trampoline_tls); // Never returns. longjmp(*recovery_trampoline, 1); } void yyerror( yyscan_t yyscanner, HEX_LEX_ENVIRONMENT* lex_env, const char *error_message) { // if lex_env->last_error was set to some error code before // don't overwrite it, we are interested in the first error, not in // subsequent errors like "syntax error, unexpected $end" caused by // early parser termination. if (lex_env->last_error == ERROR_SUCCESS) { lex_env->last_error = ERROR_INVALID_HEX_STRING; strlcpy( lex_env->last_error_message, error_message, sizeof(lex_env->last_error_message)); } } int yr_parse_hex_string( const char* hex_string, RE_AST** re_ast, RE_ERROR* error) { yyscan_t yyscanner; jmp_buf recovery_trampoline; HEX_LEX_ENVIRONMENT lex_env; lex_env.last_error = ERROR_SUCCESS; lex_env.inside_or = 0; yr_thread_storage_set_value( &yr_yyfatal_trampoline_tls, &recovery_trampoline); if (setjmp(recovery_trampoline) != 0) return ERROR_INTERNAL_FATAL_ERROR; FAIL_ON_ERROR(yr_re_ast_create(re_ast)); // The RE_FLAGS_FAST_REGEXP flag indicates a regular expression can be // matched by faster algorithm. These regular expressions come from hex // strings that do not contain alternatives, like in: // // { ( 01 02 | 03 04) 05 06 }. // // This flag is unset later during parsing if alternatives are used. (*re_ast)->flags |= RE_FLAGS_FAST_REGEXP; // Set RE_FLAGS_DOT_ALL because in hex strings the "dot" (?? in this case) // must match all characters including new-line. (*re_ast)->flags |= RE_FLAGS_DOT_ALL; yylex_init(&yyscanner); yyset_extra(*re_ast, yyscanner); yy_scan_string(hex_string, yyscanner); yyparse(yyscanner, &lex_env); yylex_destroy(yyscanner); if (lex_env.last_error != ERROR_SUCCESS) { strlcpy(error->message, lex_env.last_error_message, sizeof(error->message)); return lex_env.last_error; } return ERROR_SUCCESS; } yara-4.1.3/libyara/include/000077500000000000000000000000001413423160300155165ustar00rootroot00000000000000yara-4.1.3/libyara/include/yara.h000066400000000000000000000034331413423160300166260ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_YARA_H #define YR_YARA_H #include "yara/compiler.h" #include "yara/error.h" #include "yara/filemap.h" #include "yara/hash.h" #include "yara/libyara.h" #include "yara/mem.h" #include "yara/modules.h" #include "yara/object.h" #include "yara/scanner.h" #include "yara/stream.h" #include "yara/utils.h" #endif yara-4.1.3/libyara/include/yara/000077500000000000000000000000001413423160300164525ustar00rootroot00000000000000yara-4.1.3/libyara/include/yara/ahocorasick.h000066400000000000000000000051551413423160300211170ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _AHOCORASICK_H #define _AHOCORASICK_H #include #include #include // Number of bits dedicated to store the offset of the slot relative to its // own state. #define YR_AC_SLOT_OFFSET_BITS 9 // Max number of slots in the transition table. This is the maximum number of // slots that can be addressed with 23-bit indexes. #define YR_AC_MAX_TRANSITION_TABLE_SIZE 0x800000 #define YR_AC_ROOT_STATE 0 #define YR_AC_NEXT_STATE(t) (t >> YR_AC_SLOT_OFFSET_BITS) #define YR_AC_INVALID_TRANSITION(t, c) (((t) &0x1FF) != c) #define YR_AC_MAKE_TRANSITION(state, code) \ ((YR_AC_TRANSITION)( \ (((YR_AC_TRANSITION) state) << YR_AC_SLOT_OFFSET_BITS) | (code))) int yr_ac_automaton_create(YR_ARENA* arena, YR_AC_AUTOMATON** automaton); int yr_ac_automaton_destroy(YR_AC_AUTOMATON* automaton); int yr_ac_add_string( YR_AC_AUTOMATON* automaton, YR_STRING* string, uint32_t string_idx, YR_ATOM_LIST_ITEM* atom, YR_ARENA* arena); int yr_ac_compile(YR_AC_AUTOMATON* automaton, YR_ARENA* arena); void yr_ac_print_automaton(YR_AC_AUTOMATON* automaton); #endif yara-4.1.3/libyara/include/yara/arena.h000066400000000000000000000213641413423160300177170ustar00rootroot00000000000000/* Copyright (c) 2020. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_ARENA_H #define YR_ARENA_H #include #include #include #include #define EOL ((size_t) -1) #define YR_ARENA_FILE_VERSION 18 #define YR_ARENA_NULL_REF \ (YR_ARENA_REF) { UINT32_MAX, UINT32_MAX } #define YR_ARENA_IS_NULL_REF(ref) \ (memcmp(&(ref), &YR_ARENA_NULL_REF, sizeof(YR_ARENA_NULL_REF)) == 0) typedef uint32_t yr_arena_off_t; typedef struct YR_ARENA YR_ARENA; typedef struct YR_ARENA_BUFFER YR_ARENA_BUFFER; typedef struct YR_ARENA_REF YR_ARENA_REF; typedef struct YR_RELOC YR_RELOC; // The YR_ARENA_REF must be 64-bits long because this structure occupies // the place of relocatable pointers when arenas are saved to file. The pack(4) // directive ensures that fields are aligned to 4-byte boundaries and the total // size of the structure is 8 bytes. When compiling YARA for 32-bits, pointers // are 4 bytes but this structure is still 8 bytes. For that reason structures // that are saved in arenas must declare pointers using the DECLARE_REFERENCE // macro, which ensures that 8 bytes are reserved for the pointer no matter if // they are 32-bits pointers. #pragma pack(push) #pragma pack(4) struct YR_ARENA_REF { uint32_t buffer_id; yr_arena_off_t offset; }; #pragma pack(pop) struct YR_ARENA_BUFFER { // Pointer the buffer's data. uint8_t* data; // Total buffer size, including the used and unused areas. The maximum size // allowed for a buffer is 4GB because offsets in YR_ARENA_REF are 32-bit // integers. size_t size; // Number of bytes that are actually used (equal to or lower than size). size_t used; }; struct YR_RELOC { // Buffer ID associated to this relocation entry. uint32_t buffer_id; // Offset within the buffer where the relocatable pointer resides. yr_arena_off_t offset; // Pointer to the next entry in the list. struct YR_RELOC* next; }; /* YR_ARENA is the structure that represents an arena. An arena is a set of buffers in which you can write arbitrary data. When the arena is created you can specify how many buffers it will have, this number is limited by YR_MAX_ARENA_BUFFERS. Buffers are numbered 0, 1, 2, and so on, and they are referenced by its number. Initially each buffer will have a size that is determined by an argument passed to yr_create_arena, but the size will grow automatically as you write data into it. When the buffer is re-sized it may be moved to a different location in memory, which means that any pointer you hold to data contained in the buffer is valid only until the next call to a function that may cause a buffer re-sizing. For long-lived references to data in a buffer you should use a YR_ARENA_REF, which is simply a struct storing the buffer number and the offset of the data relative to the beginning of the buffer. Such YR_ARENA_REF structures are returned by all functions that allocate space or write data in a buffer. The yr_arena_get_ptr function can be used for getting a pointer to data stored in the buffer given its offset, but this pointer should be used with only in a limited scope where you can be sure that your program is not calling a function that may cause a buffer re-sizing, like yr_arena_allocate_xxx and yr_arena_write_xxx. Arenas can also keep track pointers stored in some of its buffers that point to some location within the arena (it can be to the same buffer or a different one within the same arena). This is done with yr_arena_make_ptr_relocatable, this function receives a buffer number and an offset, and it tells the arena that the pointer stored at that particular location of the buffer must be adjusted every time the memory it points to is moved around. The yr_arena_allocate_struct function is an even more powerful mechanism for tracking relocatable pointers. This function allows to allocate space in a buffer for a structure, while at the same time telling the arena which fields in the structure are relocatable pointers. */ struct YR_ARENA { // Number of users of this arena. This is set to one when the arena is // created, and can be incremented by calling yr_arena_acquire. On each call // to yr_arena_release it gets decremented by one, if xrefs reaches zero // the buffers and the YR_ARENA structures are freed. uint32_t xrefs; // Number of buffers in this arena. uint32_t num_buffers; // Status of individual buffers. YR_ARENA_BUFFER buffers[YR_MAX_ARENA_BUFFERS]; // Initial size for each buffer. size_t initial_buffer_size; // Head of the list containing relocation entries. YR_RELOC* reloc_list_head; // Tail of the list containing relocation entries. YR_RELOC* reloc_list_tail; }; // Creates an arena with the specified number of buffers and takes ownership of // it. Initially each buffer is empty, the first time that some data is written // into a buffer at least initial_buffer_size are reserved for the buffer. int yr_arena_create( uint32_t num_buffers, size_t initial_buffer_size, YR_ARENA** arena); // Takes ownership of the arena. void yr_arena_acquire(YR_ARENA* arena); // Release ownership of the arena. If the number of owners drops to zero the // arena is destroyed and all its resources are freed. int yr_arena_release(YR_ARENA* arena); // Given a reference to some data within the arena, it returns a pointer to // the data. This pointer is valid only until the next call to any of the // functions that allocates space in the buffer where the data resides, like // yr_arena_allocate_xxx and yr_arena_write_xxx. These functions can cause // the buffer to be moved to different memory location and the pointer won't // valid any longer. void* yr_arena_ref_to_ptr(YR_ARENA* arena, YR_ARENA_REF* ref); // Given a pointer into the arena, it returns a reference to it. The reference // can be used with yr_arena_ref_to_ptr to obtain a pointer again. Unlike // pointers, references are during the arena's lifetime, even if the buffers // are moved to a different memory location. int yr_arena_ptr_to_ref( YR_ARENA* arena, const void* address, YR_ARENA_REF* ref); // Given a buffer number and an offset within the buffer, returns a pointer // to that offset. The same limitations explained for yr_arena_ref_to_ptr // applies for the pointers returned by this function. void* yr_arena_get_ptr( YR_ARENA* arena, uint32_t buffer_id, yr_arena_off_t offset); yr_arena_off_t yr_arena_get_current_offset(YR_ARENA* arena, uint32_t buffer_id); int yr_arena_allocate_memory( YR_ARENA* arena, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref); int yr_arena_allocate_zeroed_memory( YR_ARENA* arena, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref); int yr_arena_allocate_struct( YR_ARENA* arena, uint32_t buffer_id, size_t size, YR_ARENA_REF* ref, ...); int yr_arena_make_ptr_relocatable(YR_ARENA* arena, uint32_t buffer_id, ...); int yr_arena_write_data( YR_ARENA* arena, uint32_t buffer_id, const void* data, size_t size, YR_ARENA_REF* ref); int yr_arena_write_string( YR_ARENA* arena, uint32_t buffer_id, const char* string, YR_ARENA_REF* ref); int yr_arena_write_uint32( YR_ARENA* arena, uint32_t buffer_id, uint32_t integer, YR_ARENA_REF* ref); int yr_arena_load_stream(YR_STREAM* stream, YR_ARENA** arena); int yr_arena_save_stream(YR_ARENA* arena, YR_STREAM* stream); #endif // YR_ARENA_H yara-4.1.3/libyara/include/yara/atoms.h000066400000000000000000000072321413423160300177520ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_ATOMS_H #define YR_ATOMS_H #include #include #define ATOM_TREE_LEAF 1 #define ATOM_TREE_AND 2 #define ATOM_TREE_OR 3 typedef struct YR_ATOM YR_ATOM; typedef struct YR_ATOM_TREE_NODE YR_ATOM_TREE_NODE; typedef struct YR_ATOM_TREE YR_ATOM_TREE; typedef struct YR_ATOM_LIST_ITEM YR_ATOM_LIST_ITEM; typedef struct YR_ATOM_QUALITY_TABLE_ENTRY YR_ATOM_QUALITY_TABLE_ENTRY; typedef struct YR_ATOMS_CONFIG YR_ATOMS_CONFIG; struct YR_ATOM { uint8_t length; uint8_t bytes[YR_MAX_ATOM_LENGTH]; uint8_t mask[YR_MAX_ATOM_LENGTH]; }; struct YR_ATOM_TREE_NODE { uint8_t type; YR_ATOM atom; // RE nodes that correspond to each byte in the atom. RE_NODE* re_nodes[YR_MAX_ATOM_LENGTH]; YR_ATOM_TREE_NODE* children_head; YR_ATOM_TREE_NODE* children_tail; YR_ATOM_TREE_NODE* next_sibling; }; struct YR_ATOM_TREE { YR_ATOM_TREE_NODE* root_node; }; struct YR_ATOM_LIST_ITEM { YR_ATOM atom; uint16_t backtrack; YR_ARENA_REF forward_code_ref; YR_ARENA_REF backward_code_ref; YR_ATOM_LIST_ITEM* next; }; #pragma pack(push) #pragma pack(1) struct YR_ATOM_QUALITY_TABLE_ENTRY { const uint8_t atom[YR_MAX_ATOM_LENGTH]; const uint8_t quality; }; #pragma pack(pop) typedef int (*YR_ATOMS_QUALITY_FUNC)(YR_ATOMS_CONFIG* config, YR_ATOM* atom); struct YR_ATOMS_CONFIG { YR_ATOMS_QUALITY_FUNC get_atom_quality; YR_ATOM_QUALITY_TABLE_ENTRY* quality_table; int quality_warning_threshold; int quality_table_entries; bool free_quality_table; }; int yr_atoms_extract_from_re( YR_ATOMS_CONFIG* config, RE_AST* re_ast, YR_MODIFIER modifier, YR_ATOM_LIST_ITEM** atoms, int* min_atom_quality); int yr_atoms_extract_from_string( YR_ATOMS_CONFIG* config, uint8_t* string, int string_length, YR_MODIFIER modifier, YR_ATOM_LIST_ITEM** atoms, int* min_atom_quality); int yr_atoms_extract_triplets(RE_NODE* re_node, YR_ATOM_LIST_ITEM** atoms); int yr_atoms_heuristic_quality(YR_ATOMS_CONFIG* config, YR_ATOM* atom); int yr_atoms_table_quality(YR_ATOMS_CONFIG* config, YR_ATOM* atom); int yr_atoms_min_quality(YR_ATOMS_CONFIG* config, YR_ATOM_LIST_ITEM* atom_list); void yr_atoms_list_destroy(YR_ATOM_LIST_ITEM* list_head); #endif yara-4.1.3/libyara/include/yara/base64.h000066400000000000000000000035111413423160300177070ustar00rootroot00000000000000/* Copyright (c) 2020. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_BASE64_H #define YR_BASE64_H #include #include #include typedef struct BASE64_NODE BASE64_NODE; struct BASE64_NODE { SIZED_STRING* str; int escaped; BASE64_NODE* next; }; int yr_base64_ast_from_string( SIZED_STRING* in_str, YR_MODIFIER modifier, RE_AST** re_ast, RE_ERROR* error); #endif yara-4.1.3/libyara/include/yara/bitmask.h000066400000000000000000000066241413423160300202650ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_BITMASK_H #define YR_BITMASK_H #include // // Utility macros for working with bitmaps. // // Declare a bitmask of n bits: // YR_BITMASK my_bitmask[YR_BITMASK_SIZE(n)]; // // Clear all bits: // yr_bitmask_clear_all(my_bitmask) // // Set bit n to 1: // yr_bitmask_set(my_bitmask, n) // // Clear bit n (set to 0): // yr_bitmask_clear(my_bitmask, n) // // Check if bit n is set: // yr_bitmask_is_set(my_bitmask, n) // #define YR_BITMASK unsigned long #define YR_BITMASK_SLOT_BITS (sizeof(YR_BITMASK) * 8) #define YR_BITMASK_SIZE(n) (((n) / (YR_BITMASK_SLOT_BITS)) + 1) #define yr_bitmask_set(bm, i) \ do \ { \ (bm)[(i) / YR_BITMASK_SLOT_BITS] |= 1UL << ((i) % YR_BITMASK_SLOT_BITS); \ } while (0) #define yr_bitmask_clear(bm, i) \ do \ { \ (bm)[(i) / YR_BITMASK_SLOT_BITS] &= ~( \ 1UL << ((i) % YR_BITMASK_SLOT_BITS)); \ } while (0) #define yr_bitmask_clear_all(bm) memset(bm, 0, sizeof(bm)) #define yr_bitmask_is_set(bm, i) \ ((bm)[(i) / YR_BITMASK_SLOT_BITS] & (1UL << ((i) % YR_BITMASK_SLOT_BITS))) #define yr_bitmask_is_not_set(bm, i) (!yr_bitmask_is_set(bm, i)) #define yr_bitmask_print(bm) \ { \ int i; \ for (i = 0; i < sizeof(bm) / sizeof(bm[0]); i++) \ { \ printf("%016lX\n", bm[i]); \ } \ } uint32_t yr_bitmask_find_non_colliding_offset( YR_BITMASK* a, YR_BITMASK* b, uint32_t len_a, uint32_t len_b, uint32_t* off_a); #endif yara-4.1.3/libyara/include/yara/compiler.h000066400000000000000000000314021413423160300204350ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_COMPILER_H #define YR_COMPILER_H #include #include #include #include #include #include #include #define YARA_ERROR_LEVEL_ERROR 0 #define YARA_ERROR_LEVEL_WARNING 1 // Expression type constants are powers of two because they are used as flags. #define EXPRESSION_TYPE_UNKNOWN 0 #define EXPRESSION_TYPE_BOOLEAN 1 #define EXPRESSION_TYPE_INTEGER 2 #define EXPRESSION_TYPE_STRING 4 #define EXPRESSION_TYPE_REGEXP 8 #define EXPRESSION_TYPE_OBJECT 16 #define EXPRESSION_TYPE_FLOAT 32 // The compiler uses an arena to store the data it generates during the // compilation. Each buffer in the arena is used for storing a different type // of data. The following identifiers indicate the purpose of each buffer. #define YR_NAMESPACES_TABLE 0 #define YR_RULES_TABLE 1 #define YR_METAS_TABLE 2 #define YR_STRINGS_TABLE 3 #define YR_EXTERNAL_VARIABLES_TABLE 4 #define YR_SZ_POOL 5 #define YR_CODE_SECTION 6 #define YR_RE_CODE_SECTION 7 #define YR_AC_TRANSITION_TABLE 8 #define YR_AC_STATE_MATCHES_TABLE 9 #define YR_AC_STATE_MATCHES_POOL 10 #define YR_SUMMARY_SECTION 11 // This is the number of buffers used by the compiler, should match the number // of items in the list above. #define YR_NUM_SECTIONS 12 // Number of variables used by loops. This doesn't include user defined // variables. #define YR_INTERNAL_LOOP_VARS 3 typedef struct _YR_EXPRESSION { int type; union { int64_t integer; YR_OBJECT* object; YR_ARENA_REF sized_string_ref; } value; // An expression can have an associated identifier, if "ptr" is not NULL it // points to the identifier name, if it is NULL, then "ref" holds a reference // to the identifier within YR_SZ_POOL. When the identifier is in YR_SZ_POOL // a pointer can't be used as the YR_SZ_POOL can be moved to a different // memory location. struct { const char* ptr; YR_ARENA_REF ref; } identifier; } YR_EXPRESSION; typedef void (*YR_COMPILER_CALLBACK_FUNC)( int error_level, const char* file_name, int line_number, const YR_RULE* rule, const char* message, void* user_data); typedef const char* (*YR_COMPILER_INCLUDE_CALLBACK_FUNC)( const char* include_name, const char* calling_rule_filename, const char* calling_rule_namespace, void* user_data); typedef void (*YR_COMPILER_INCLUDE_FREE_FUNC)( const char* callback_result_ptr, void* user_data); typedef void (*YR_COMPILER_RE_AST_CALLBACK_FUNC)( const YR_RULE* rule, const char* string_identifier, const RE_AST* re_ast, void* user_data); typedef struct _YR_FIXUP { YR_ARENA_REF ref; struct _YR_FIXUP* next; } YR_FIXUP; // Each "for" loop in the condition has an associated context which holds // information about loop, like the target address for the jump instruction // that goes back to the beginning of the loop and the local variables used // by the loop. typedef struct _YR_LOOP_CONTEXT { // Reference indicating the the place in the code where the loop starts. The // loop goes back to this address on each iteration. YR_ARENA_REF start_ref; // vars_count is the number of local variables defined by the loop, and vars // is an array of expressions with the identifier and type for each of those // local variables. int vars_count; YR_EXPRESSION vars[YR_MAX_LOOP_VARS]; // vars_internal_count is the number of variables used by the loop which are // not defined by the rule itself but that are necessary for keeping the // loop's state. One example is the iteration counter. int vars_internal_count; } YR_LOOP_CONTEXT; typedef struct _YR_COMPILER { // Arena that contains the data generated by the compiled. The arena has // the following buffers: // // YR_SUMMARY_SECTION: // A YR_SUMMARY struct. // YR_RULES_TABLE: // An array of YR_RULE structures, one per each rule. // YR_STRINGS_TABLE: // An array of YR_STRING structures, one per each string. // YR_METAS_TABLE: // An array of YR_META structures, one per each meta definition. // YR_NAMESPACES_TABLE: // An array of YR_NAMESPACE structures, one per each namespace. // YR_EXTERNAL_VARIABLES_TABLE: // An array of YR_EXTERNAL_VARIABLE structures, one per each external // variable defined. // YR_SZ_POOL: // A collection of null-terminated strings. This buffer contains // identifiers, literal strings, and in general any null-terminated // string referenced by other data structures. // YR_CODE_SECTION: // The code for the condition section of all the rules. This is the // code executed by yr_execute_code. // YR_RE_CODE_SECTION: // Similar to YR_CODE_SECTION, but it contains the code for regular // expressions. This is the code executed by yr_re_exec and // yr_re_fast_exec. // YR_AC_TRANSITION_TABLE: // An array of uint32_t containing the Aho-Corasick transition table. // See comment in _yr_ac_build_transition_table for details. // YR_AC_STATE_MATCHES_TABLE: // An array of uint32_t with the same number of items than the transition // table. If entry N in the transition table corresponds to some // Aho-Corasick state, the N-th item in this array has the index within // the matches pool where the list of matches for that state begins. // YR_AC_STATE_MATCHES_POOL: // An array of YR_AC_MATCH structures. // YR_ARENA* arena; // Index of the rule being compiled in the array of YR_RULE structures // stored in YR_RULES_TABLE. If this is MAX_UINT32 the compiler is not // parsing a rule. uint32_t current_rule_idx; // Index of the rule that comes next during parsing. uint32_t next_rule_idx; // Index of the string being compiled in the array of YR_STRING structures // stored in YR_STRINGS_TABLE. uint32_t current_string_idx; // Index of the current namespace in the array of YR_NAMESPACE structures // stored in YR_NAMESPACES_TABLE. uint32_t current_namespace_idx; // Index of the current meta in the array of YR_META structures stored in // YR_METAS_TABLE. uint32_t current_meta_idx; // Pointer to a YR_RULES structure that represents the compiled rules. This // is what yr_compiler_get_rules returns. Once these rules are generated you // can't call any of the yr_compiler_add_xxx functions. YR_RULES* rules; int errors; int current_line; int last_error; int last_error_line; jmp_buf error_recovery; YR_AC_AUTOMATON* automaton; YR_HASH_TABLE* rules_table; YR_HASH_TABLE* objects_table; YR_HASH_TABLE* strings_table; // Hash table that contains all the strings that has been written to the // YR_SZ_POOL buffer in the compiler's arena. Values in the hash table are // the offset within the YR_SZ_POOL where the string resides. This allows to // know is some string has already been written in order to reuse instead of // writting it again. YR_HASH_TABLE* sz_table; YR_FIXUP* fixup_stack_head; int num_namespaces; YR_LOOP_CONTEXT loop[YR_MAX_LOOP_NESTING]; int loop_index; int loop_for_of_var_index; char* file_name_stack[YR_MAX_INCLUDE_DEPTH]; int file_name_stack_ptr; char last_error_extra_info[YR_MAX_COMPILER_ERROR_EXTRA_INFO]; // This buffer is used by the lexer for accumulating text strings. Those // strings are copied from flex's internal variables. lex_buf_ptr points to // the end of the string and lex_buf_len contains the number of bytes that // have been copied into lex_buf. char lex_buf[YR_LEX_BUF_SIZE]; char* lex_buf_ptr; unsigned short lex_buf_len; char include_base_dir[MAX_PATH]; void* user_data; void* incl_clbk_user_data; void* re_ast_clbk_user_data; YR_COMPILER_CALLBACK_FUNC callback; YR_COMPILER_INCLUDE_CALLBACK_FUNC include_callback; YR_COMPILER_INCLUDE_FREE_FUNC include_free; YR_COMPILER_RE_AST_CALLBACK_FUNC re_ast_callback; YR_ATOMS_CONFIG atoms_config; } YR_COMPILER; #define yr_compiler_set_error_extra_info(compiler, info) \ strlcpy( \ compiler->last_error_extra_info, \ info, \ sizeof(compiler->last_error_extra_info)); #define yr_compiler_set_error_extra_info_fmt(compiler, fmt, ...) \ snprintf( \ compiler->last_error_extra_info, \ sizeof(compiler->last_error_extra_info), \ fmt, \ __VA_ARGS__); int _yr_compiler_push_file_name(YR_COMPILER* compiler, const char* file_name); void _yr_compiler_pop_file_name(YR_COMPILER* compiler); int _yr_compiler_get_var_frame(YR_COMPILER* compiler); const char* _yr_compiler_default_include_callback( const char* include_name, const char* calling_rule_filename, const char* calling_rule_namespace, void* user_data); YR_RULE* _yr_compiler_get_rule_by_idx(YR_COMPILER* compiler, uint32_t rule_idx); int _yr_compiler_store_string( YR_COMPILER* compiler, const char* string, YR_ARENA_REF* ref); int _yr_compiler_store_data( YR_COMPILER* compiler, const void* data, size_t data_length, YR_ARENA_REF* ref); YR_API int yr_compiler_create(YR_COMPILER** compiler); YR_API void yr_compiler_destroy(YR_COMPILER* compiler); YR_API void yr_compiler_set_callback( YR_COMPILER* compiler, YR_COMPILER_CALLBACK_FUNC callback, void* user_data); YR_API void yr_compiler_set_include_callback( YR_COMPILER* compiler, YR_COMPILER_INCLUDE_CALLBACK_FUNC include_callback, YR_COMPILER_INCLUDE_FREE_FUNC include_free, void* user_data); YR_API void yr_compiler_set_re_ast_callback( YR_COMPILER* compiler, YR_COMPILER_RE_AST_CALLBACK_FUNC re_ast_callback, void* user_data); YR_API void yr_compiler_set_atom_quality_table( YR_COMPILER* compiler, const void* table, int entries, unsigned char warning_threshold); YR_API int yr_compiler_load_atom_quality_table( YR_COMPILER* compiler, const char* filename, unsigned char warning_threshold); YR_API int yr_compiler_add_file( YR_COMPILER* compiler, FILE* rules_file, const char* namespace_, const char* file_name); YR_API int yr_compiler_add_fd( YR_COMPILER* compiler, YR_FILE_DESCRIPTOR rules_fd, const char* namespace_, const char* file_name); YR_API int yr_compiler_add_string( YR_COMPILER* compiler, const char* rules_string, const char* namespace_); YR_API char* yr_compiler_get_error_message( YR_COMPILER* compiler, char* buffer, int buffer_size); YR_API char* yr_compiler_get_current_file_name(YR_COMPILER* compiler); YR_API int yr_compiler_define_integer_variable( YR_COMPILER* compiler, const char* identifier, int64_t value); YR_API int yr_compiler_define_boolean_variable( YR_COMPILER* compiler, const char* identifier, int value); YR_API int yr_compiler_define_float_variable( YR_COMPILER* compiler, const char* identifier, double value); YR_API int yr_compiler_define_string_variable( YR_COMPILER* compiler, const char* identifier, const char* value); YR_API int yr_compiler_get_rules(YR_COMPILER* compiler, YR_RULES** rules); #endif yara-4.1.3/libyara/include/yara/dex.h000066400000000000000000000054521413423160300174110ustar00rootroot00000000000000#ifndef _DEX_H #define _DEX_H #include #include #include #define DEX_FILE_MAGIC_035 "dex\n035\x00" #define DEX_FILE_MAGIC_036 "dex\n036\x00" #define DEX_FILE_MAGIC_037 "dex\n037\x00" #define DEX_FILE_MAGIC_038 "dex\n038\x00" #define DEX_FILE_MAGIC_039 "dex\n039\x00" #pragma pack(push, 1) typedef struct { uint8_t magic[8]; uint32_t checksum; uint8_t signature[20]; uint32_t file_size; uint32_t header_size; uint32_t endian_tag; uint32_t link_size; uint32_t link_offset; uint32_t map_offset; uint32_t string_ids_size; uint32_t string_ids_offset; uint32_t type_ids_size; uint32_t type_ids_offset; uint32_t proto_ids_size; uint32_t proto_ids_offset; uint32_t field_ids_size; uint32_t field_ids_offset; uint32_t method_ids_size; uint32_t method_ids_offset; uint32_t class_defs_size; uint32_t class_defs_offset; uint32_t data_size; uint32_t data_offset; } dex_header_t; typedef struct { uint32_t string_data_offset; } string_id_item_t; typedef struct { uint32_t utf16_size; } string_data_item_t; typedef struct { uint32_t descriptor_idx; } type_id_item_t; typedef struct { uint32_t shorty_idx; uint32_t return_type_idx; uint32_t parameters_offset; } proto_id_item_t; typedef struct { uint16_t class_idx; uint16_t type_idx; uint32_t name_idx; } field_id_item_t; typedef struct { uint16_t class_idx; uint16_t proto_idx; uint32_t name_idx; } method_id_item_t; typedef struct { uint32_t class_idx; uint32_t access_flags; uint32_t super_class_idx; uint32_t interfaces_offset; uint32_t source_file_idx; uint32_t annotations_offset; uint32_t class_data_offset; uint32_t static_values_offset; } class_id_item_t; typedef struct { uint32_t static_fields_size; uint32_t instance_fields_size; uint32_t direct_methods_size; uint32_t virtual_methods_size; } class_data_item_t; typedef struct { uint32_t field_idx_diff; uint32_t access_flags; } encoded_field_t; typedef struct { uint32_t method_idx_diff; uint32_t access_flags; uint32_t code_off; } encoded_method_t; typedef struct { uint16_t registers_size; uint16_t ins_size; uint16_t outs_size; uint16_t tries_size; uint32_t debug_info_off; uint32_t insns_size; } code_item_t; typedef struct { uint16_t type; uint16_t unused; uint32_t size; uint32_t offset; } map_item_t; typedef struct _DEX { const uint8_t* data; size_t data_size; dex_header_t* header; YR_OBJECT* object; } DEX; #define fits_in_dex(dex, pointer, size) \ ((size_t) size <= dex->data_size && (uint8_t*) (pointer) >= dex->data && \ (uint8_t*) (pointer) <= dex->data + dex->data_size - size) #define struct_fits_in_dex(dex, pointer, struct_type) \ fits_in_dex(dex, pointer, sizeof(struct_type)) #pragma pack(pop) #endif yara-4.1.3/libyara/include/yara/dotnet.h000066400000000000000000000203331413423160300201210ustar00rootroot00000000000000#ifndef YR_DOTNET_H #define YR_DOTNET_H #include #pragma pack(push, 1) // // CLI header. // ECMA-335 Section II.25.3.3 // typedef struct _CLI_HEADER { DWORD Size; // Called "Cb" in documentation. WORD MajorRuntimeVersion; WORD MinorRuntimeVersion; IMAGE_DATA_DIRECTORY MetaData; DWORD Flags; DWORD EntryPointToken; IMAGE_DATA_DIRECTORY Resources; IMAGE_DATA_DIRECTORY StrongNameSignature; ULONGLONG CodeManagerTable; IMAGE_DATA_DIRECTORY VTableFixups; ULONGLONG ExportAddressTableJumps; ULONGLONG ManagedNativeHeader; } CLI_HEADER, *PCLI_HEADER; #define NET_METADATA_MAGIC 0x424a5342 // // CLI MetaData // ECMA-335 Section II.24.2.1 // // Note: This is only part of the struct, as the rest of it is variable length. // typedef struct _NET_METADATA { DWORD Magic; WORD MajorVersion; WORD MinorVersion; DWORD Reserved; DWORD Length; char Version[0]; } NET_METADATA, *PNET_METADATA; #define DOTNET_STREAM_NAME_SIZE 32 // // CLI Stream Header // ECMA-335 Section II.24.2.2 // typedef struct _STREAM_HEADER { DWORD Offset; DWORD Size; char Name[0]; } STREAM_HEADER, *PSTREAM_HEADER; // // CLI #~ Stream Header // ECMA-335 Section II.24.2.6 // typedef struct _TILDE_HEADER { DWORD Reserved1; BYTE MajorVersion; BYTE MinorVersion; BYTE HeapSizes; BYTE Reserved2; ULONGLONG Valid; ULONGLONG Sorted; } TILDE_HEADER, *PTILDE_HEADER; // These are the bit positions in Valid which will be set if the table // exists. #define BIT_MODULE 0x00 #define BIT_TYPEREF 0x01 #define BIT_TYPEDEF 0x02 #define BIT_FIELDPTR 0x03 // Not documented in ECMA-335 #define BIT_FIELD 0x04 #define BIT_METHODDEFPTR 0x05 // Not documented in ECMA-335 #define BIT_METHODDEF 0x06 #define BIT_PARAMPTR 0x07 // Not documented in ECMA-335 #define BIT_PARAM 0x08 #define BIT_INTERFACEIMPL 0x09 #define BIT_MEMBERREF 0x0A #define BIT_CONSTANT 0x0B #define BIT_CUSTOMATTRIBUTE 0x0C #define BIT_FIELDMARSHAL 0x0D #define BIT_DECLSECURITY 0x0E #define BIT_CLASSLAYOUT 0x0F #define BIT_FIELDLAYOUT 0x10 #define BIT_STANDALONESIG 0x11 #define BIT_EVENTMAP 0x12 #define BIT_EVENTPTR 0x13 // Not documented in ECMA-335 #define BIT_EVENT 0x14 #define BIT_PROPERTYMAP 0x15 #define BIT_PROPERTYPTR 0x16 // Not documented in ECMA-335 #define BIT_PROPERTY 0x17 #define BIT_METHODSEMANTICS 0x18 #define BIT_METHODIMPL 0x19 #define BIT_MODULEREF 0x1A #define BIT_TYPESPEC 0x1B #define BIT_IMPLMAP 0x1C #define BIT_FIELDRVA 0x1D #define BIT_ENCLOG 0x1E // Not documented in ECMA-335 #define BIT_ENCMAP 0x1F // Not documented in ECMA-335 #define BIT_ASSEMBLY 0x20 #define BIT_ASSEMBLYPROCESSOR 0x21 #define BIT_ASSEMBLYOS 0x22 #define BIT_ASSEMBLYREF 0x23 #define BIT_ASSEMBLYREFPROCESSOR 0x24 #define BIT_ASSEMBLYREFOS 0x25 #define BIT_FILE 0x26 #define BIT_EXPORTEDTYPE 0x27 #define BIT_MANIFESTRESOURCE 0x28 #define BIT_NESTEDCLASS 0x29 #define BIT_GENERICPARAM 0x2A #define BIT_METHODSPEC 0x2B #define BIT_GENERICPARAMCONSTRAINT 0x2C // These are not documented in ECMA-335 nor is it clear what the format is. // They are for debugging information as far as I can tell. //#define BIT_DOCUMENT 0x30 //#define BIT_METHODDEBUGINFORMATION 0x31 //#define BIT_LOCALSCOPE 0x32 //#define BIT_LOCALVARIABLE 0x33 //#define BIT_LOCALCONSTANT 0x34 //#define BIT_IMPORTSCOPE 0x35 //#define BIT_STATEMACHINEMETHOD 0x36 // // Element types. Note this is not a complete list as we aren't parsing all of // them. This only includes the ones we care about. // ECMA-335 Section II.23.1.16 // #define ELEMENT_TYPE_STRING 0x0E // The string length of a typelib attribute is at most 0xFF. #define MAX_TYPELIB_SIZE 0xFF // // Module table // ECMA-335 Section II.22.30 // typedef struct _MODULE_TABLE { WORD Generation; union { WORD Name_Short; DWORD Name_Long; } Name; union { WORD Mvid_Short; DWORD Mvid_Long; } Mvid; union { WORD EncId_Short; DWORD EncId_Long; } EncId; union { WORD EncBaseId_Short; DWORD EncBaseId_Long; } EncBaseId; } MODULE_TABLE, *PMODULE_TABLE; // // Assembly Table // ECMA-335 Section II.22.2 // typedef struct _ASSEMBLY_TABLE { DWORD HashAlgId; WORD MajorVersion; WORD MinorVersion; WORD BuildNumber; WORD RevisionNumber; DWORD Flags; union { WORD PublicKey_Short; DWORD PublicKey_Long; } PublicKey; union { WORD Name_Short; DWORD Name_Long; } Name; } ASSEMBLY_TABLE, *PASSEMBLY_TABLE; // // Assembly Reference Table // ECMA-335 Section II.22.5 // typedef struct _ASSEMBLYREF_TABLE { WORD MajorVersion; WORD MinorVersion; WORD BuildNumber; WORD RevisionNumber; DWORD Flags; union { WORD PublicKeyOrToken_Short; DWORD PublicKeyOrToken_Long; } PublicKeyOrToken; union { WORD Name_Short; DWORD Name_Long; } Name; } ASSEMBLYREF_TABLE, *PASSEMBLYREF_TABLE; // // Manifest Resource Table // ECMA-335 Section II.22.24 // typedef struct _MANIFESTRESOURCE_TABLE { DWORD Offset; DWORD Flags; union { WORD Name_Short; DWORD Name_Long; } Name; union { WORD Implementation_Short; DWORD Implementation_Long; } Implementation; } MANIFESTRESOURCE_TABLE, *PMANIFESTRESOURCE_TABLE; // // ModuleRef Table // ECMA-335 Section II.22.31 // // This is a short table, but necessary because the field size can change. // typedef struct _MODULEREF_TABLE { union { WORD Name_Short; DWORD Name_Long; } Name; } MODULEREF_TABLE, *PMODULEREF_TABLE; // // FieldRVA Table // ECMA-335 Section II.22.18 // typedef struct _FIELDRVA_TABLE { DWORD RVA; union { WORD Field_Short; DWORD Field_LONG; } Field; } FIELDRVA_TABLE, *PFIELDRVA_TABLE; // // CustomAttribute Table // ECMA-335 Section II.22.10 // typedef struct _CUSTOMATTRIBUTE_TABLE { union { WORD Parent_Short; DWORD Parent_Long; } Parent; union { WORD Type_Short; DWORD Type_Long; } Type; union { WORD Value_Short; DWORD Value_Long; } Value; } CUSTOMATTRIBUTE_TABLE, *PCUSTOMATTRIBUTE_TABLE; // // Constant TAble // ECMA-335 Section II.22.9 // typedef struct _CONSTANT_TABLE { WORD Type; union { WORD Parent_Short; DWORD Parent_Long; } Parent; union { WORD Value_Short; DWORD Value_Long; } Value; } CONSTANT_TABLE, *PCONSTANT_TABLE; // Used to return offsets to the various headers. typedef struct _STREAMS { PSTREAM_HEADER guid; PSTREAM_HEADER tilde; PSTREAM_HEADER string; PSTREAM_HEADER blob; PSTREAM_HEADER us; } STREAMS, *PSTREAMS; // Used to return the value of parsing a #US or #Blob entry. // ECMA-335 Section II.24.2.4 typedef struct _BLOB_PARSE_RESULT { uint8_t size; // Number of bytes parsed. This is the new offset. DWORD length; // Value of the bytes parsed. This is the blob length. } BLOB_PARSE_RESULT, *PBLOB_PARSE_RESULT; // Used to store the number of rows of each table. typedef struct _ROWS { uint32_t module; uint32_t moduleref; uint32_t assemblyref; uint32_t typeref; uint32_t methoddef; uint32_t memberref; uint32_t typedef_; uint32_t typespec; uint32_t field; uint32_t param; uint32_t property; uint32_t interfaceimpl; uint32_t event; uint32_t standalonesig; uint32_t assembly; uint32_t file; uint32_t exportedtype; uint32_t manifestresource; uint32_t genericparam; uint32_t genericparamconstraint; uint32_t methodspec; uint32_t assemblyrefprocessor; } ROWS, *PROWS; // Used to store the index sizes for the various tables. typedef struct _INDEX_SIZES { uint8_t string; uint8_t guid; uint8_t blob; uint8_t field; uint8_t methoddef; uint8_t memberref; uint8_t param; uint8_t event; uint8_t typedef_; uint8_t property; uint8_t moduleref; uint8_t assemblyrefprocessor; uint8_t assemblyref; uint8_t genericparam; } INDEX_SIZES, *PINDEX_SIZES; #pragma pack(pop) #endif yara-4.1.3/libyara/include/yara/elf.h000066400000000000000000000236711413423160300174020ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _ELF_H #define _ELF_H #include // 32-bit ELF base types typedef uint32_t elf32_addr_t; typedef uint16_t elf32_half_t; typedef uint32_t elf32_off_t; typedef uint32_t elf32_word_t; // 64-bit ELF base types typedef uint64_t elf64_addr_t; typedef uint16_t elf64_half_t; typedef uint64_t elf64_off_t; typedef uint32_t elf64_word_t; typedef uint64_t elf64_xword_t; #define ELF_MAGIC 0x464C457F #define ELF_ET_NONE 0x0000 // no type #define ELF_ET_REL 0x0001 // relocatable #define ELF_ET_EXEC 0x0002 // executable #define ELF_ET_DYN 0x0003 // Shared-Object-File #define ELF_ET_CORE 0x0004 // Corefile #define ELF_ET_LOPROC 0xFF00 // Processor-specific #define ELF_ET_HIPROC 0x00FF // Processor-specific #define ELF_EM_NONE 0x0000 // no type #define ELF_EM_M32 0x0001 // AT&T WE 32100 #define ELF_EM_SPARC 0x0002 // SPARC #define ELF_EM_386 0x0003 // Intel 80386 #define ELF_EM_68K 0x0004 // Motorola 68000 #define ELF_EM_88K 0x0005 // Motorola 88000 #define ELF_EM_860 0x0007 // Intel 80860 #define ELF_EM_MIPS 0x0008 // MIPS I Architecture #define ELF_EM_MIPS_RS3_LE 0x000A // MIPS RS3000 Little-endian #define ELF_EM_PPC 0x0014 // PowerPC #define ELF_EM_PPC64 0x0015 // 64-bit PowerPC #define ELF_EM_ARM 0x0028 // ARM #define ELF_EM_X86_64 0x003E // AMD/Intel x86_64 #define ELF_EM_AARCH64 0x00B7 // 64-bit ARM #define ELF_CLASS_NONE 0x0000 #define ELF_CLASS_32 0x0001 // 32bit file #define ELF_CLASS_64 0x0002 // 64bit file #define ELF_DATA_NONE 0x0000 #define ELF_DATA_2LSB 0x0001 #define ELF_DATA_2MSB 0x002 #define ELF_SHT_NULL 0 // Section header table entry unused #define ELF_SHT_PROGBITS 1 // Program data #define ELF_SHT_SYMTAB 2 // Symbol table #define ELF_SHT_STRTAB 3 // String table #define ELF_SHT_RELA 4 // Relocation entries with addends #define ELF_SHT_HASH 5 // Symbol hash table #define ELF_SHT_DYNAMIC 6 // Dynamic linking information #define ELF_SHT_NOTE 7 // Notes #define ELF_SHT_NOBITS 8 // Program space with no data (bss) #define ELF_SHT_REL 9 // Relocation entries, no addends #define ELF_SHT_SHLIB 10 // Reserved #define ELF_SHT_DYNSYM 11 // Dynamic linker symbol table #define ELF_SHT_NUM 12 // Number of defined types #define ELF_SHF_WRITE 0x1 // Section is writable #define ELF_SHF_ALLOC 0x2 // Section is present during execution #define ELF_SHF_EXECINSTR 0x4 // Section contains executable instructions #define ELF_SHN_LORESERVE 0xFF00 #define ELF_PT_NULL 0 // The array element is unused #define ELF_PT_LOAD 1 // Loadable segment #define ELF_PT_DYNAMIC 2 // Segment contains dynamic linking info #define ELF_PT_INTERP 3 // Contains interpreter pathname #define ELF_PT_NOTE 4 // Location & size of auxiliary info #define ELF_PT_SHLIB 5 // Reserved, unspecified semantics #define ELF_PT_PHDR 6 // Location and size of program header table #define ELF_PT_TLS 7 // Thread-Local Storage #define ELF_PT_GNU_EH_FRAME 0x6474e550 #define ELF_PT_GNU_STACK 0x6474e551 #define ELF_DT_NULL 0 // End of the dynamic entries #define ELF_DT_NEEDED 1 // Name of needed library #define ELF_DT_PLTRELSZ 2 // Size in bytes of PLT relocs #define ELF_DT_PLTGOT 3 // Processor defined value */ #define ELF_DT_HASH 4 // Address of symbol hash table #define ELF_DT_STRTAB 5 // Address of string table #define ELF_DT_SYMTAB 6 // Address of symbol table #define ELF_DT_RELA 7 // Address of Rela relocs #define ELF_DT_RELASZ 8 // Total size of Rela relocs #define ELF_DT_RELAENT 9 // Size of one Rela reloc #define ELF_DT_STRSZ 10 // Size of string table #define ELF_DT_SYMENT 11 // Size of one symbol table entry #define ELF_DT_INIT 12 // Address of init function #define ELF_DT_FINI 13 // Address of termination function #define ELF_DT_SONAME 14 // Name of shared object #define ELF_DT_RPATH 15 // Library search path (deprecated) #define ELF_DT_SYMBOLIC 16 // Start symbol search here #define ELF_DT_REL 17 // Address of Rel relocs #define ELF_DT_RELSZ 18 // Total size of Rel relocs #define ELF_DT_RELENT 19 // Size of one Rel reloc #define ELF_DT_PLTREL 20 // Type of reloc in PLT #define ELF_DT_DEBUG 21 // For debugging; unspecified #define ELF_DT_TEXTREL 22 // Reloc might modify .text #define ELF_DT_JMPREL 23 // Address of PLT relocs #define ELF_DT_BIND_NOW 24 // Process relocations of object #define ELF_DT_INIT_ARRAY 25 // Array with addresses of init fct #define ELF_DT_FINI_ARRAY 26 // Array with addresses of fini fct #define ELF_DT_INIT_ARRAYSZ 27 // Size in bytes of DT_INIT_ARRAY #define ELF_DT_FINI_ARRAYSZ 28 // Size in bytes of DT_FINI_ARRAY #define ELF_DT_RUNPATH 29 // Library search path #define ELF_DT_FLAGS 30 // Flags for the object being loaded #define ELF_DT_ENCODING 32 // Start of encoded range #define ELF_STT_NOTYPE 0 // Symbol type is unspecified #define ELF_STT_OBJECT 1 // Symbol is a data object #define ELF_STT_FUNC 2 // Symbol is a code object #define ELF_STT_SECTION 3 // Symbol associated with a section #define ELF_STT_FILE 4 // Symbol's name is file name #define ELF_STT_COMMON 5 // Symbol is a common data object #define ELF_STT_TLS 6 // Symbol is thread-local data object #define ELF_STB_LOCAL 0 // Local symbol #define ELF_STB_GLOBAL 1 // Global symbol #define ELF_STB_WEAK 2 // Weak symbol #define ELF_PF_X 0x1 // Segment is executable #define ELF_PF_W 0x2 // Segment is writable #define ELF_PF_R 0x4 // Segment is readable #define ELF_PN_XNUM 0xffff #pragma pack(push, 1) typedef struct { uint32_t magic; uint8_t _class; uint8_t data; uint8_t version; uint8_t pad[8]; uint8_t nident; } elf_ident_t; typedef struct { elf_ident_t ident; elf32_half_t type; elf32_half_t machine; elf32_word_t version; elf32_addr_t entry; elf32_off_t ph_offset; elf32_off_t sh_offset; elf32_word_t flags; elf32_half_t header_size; elf32_half_t ph_entry_size; elf32_half_t ph_entry_count; elf32_half_t sh_entry_size; elf32_half_t sh_entry_count; elf32_half_t sh_str_table_index; } elf32_header_t; typedef struct { elf_ident_t ident; elf64_half_t type; elf64_half_t machine; elf64_word_t version; elf64_addr_t entry; elf64_off_t ph_offset; elf64_off_t sh_offset; elf64_word_t flags; elf64_half_t header_size; elf64_half_t ph_entry_size; elf64_half_t ph_entry_count; elf64_half_t sh_entry_size; elf64_half_t sh_entry_count; elf64_half_t sh_str_table_index; } elf64_header_t; typedef struct { elf32_word_t type; elf32_off_t offset; elf32_addr_t virt_addr; elf32_addr_t phys_addr; elf32_word_t file_size; elf32_word_t mem_size; elf32_word_t flags; elf32_word_t alignment; } elf32_program_header_t; typedef struct { elf64_word_t type; elf64_word_t flags; elf64_off_t offset; elf64_addr_t virt_addr; elf64_addr_t phys_addr; elf64_xword_t file_size; elf64_xword_t mem_size; elf64_xword_t alignment; } elf64_program_header_t; typedef struct { elf32_word_t name; elf32_word_t type; elf32_word_t flags; elf32_addr_t addr; elf32_off_t offset; elf32_word_t size; elf32_word_t link; elf32_word_t info; elf32_word_t align; elf32_word_t entry_size; } elf32_section_header_t; typedef struct { elf64_word_t name; elf64_word_t type; elf64_xword_t flags; elf64_addr_t addr; elf64_off_t offset; elf64_xword_t size; elf64_word_t link; elf64_word_t info; elf64_xword_t align; elf64_xword_t entry_size; } elf64_section_header_t; typedef struct { elf32_word_t tag; elf32_word_t val; } elf32_dyn_t; typedef struct { elf64_xword_t tag; elf64_xword_t val; } elf64_dyn_t; typedef struct { elf32_word_t name; elf32_addr_t value; elf32_word_t size; unsigned char info; unsigned char other; elf32_half_t shndx; } elf32_sym_t; typedef struct { elf32_word_t name; unsigned char info; unsigned char other; elf32_half_t shndx; elf64_addr_t value; elf64_xword_t size; } elf64_sym_t; #pragma pack(pop) #endif yara-4.1.3/libyara/include/yara/endian.h000066400000000000000000000056071413423160300200710ustar00rootroot00000000000000/* Copyright (c) 2016. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_ENDIAN_H #define YR_ENDIAN_H #include #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) #define yr_bswap16(x) __builtin_bswap16(x) #endif #endif #if !defined(yr_bswap16) && defined(_MSC_VER) #define yr_bswap16(x) _byteswap_ushort(x) #endif #if !defined(yr_bswap16) uint16_t _yr_bswap16(uint16_t x); #define yr_bswap16(x) _yr_bswap16(x) #endif #if defined(__has_builtin) #if __has_builtin(__builtin_bswap32) #define yr_bswap32(x) __builtin_bswap32(x) #endif #endif #if !defined(yr_bswap32) && defined(_MSC_VER) #define yr_bswap32(x) _byteswap_ulong(x) #endif #if !defined(yr_bswap32) uint32_t _yr_bswap32(uint32_t x); #define yr_bswap32(x) _yr_bswap32(x) #endif #if defined(__has_builtin) #if __has_builtin(__builtin_bswap64) #define yr_bswap64(x) __builtin_bswap64(x) #endif #endif #if !defined(yr_bswap64) && defined(_MSC_VER) #define yr_bswap64(x) _byteswap_uint64(x) #endif #if !defined(yr_bswap64) uint64_t _yr_bswap64(uint64_t x); #define yr_bswap64(x) _yr_bswap64(x) #endif #if defined(WORDS_BIGENDIAN) #define yr_le16toh(x) yr_bswap16(x) #define yr_le32toh(x) yr_bswap32(x) #define yr_le64toh(x) yr_bswap64(x) #define yr_be16toh(x) (x) #define yr_be32toh(x) (x) #define yr_be64toh(x) (x) #else #define yr_le16toh(x) (x) #define yr_le32toh(x) (x) #define yr_le64toh(x) (x) #define yr_be16toh(x) yr_bswap16(x) #define yr_be32toh(x) yr_bswap32(x) #define yr_be64toh(x) yr_bswap64(x) #endif #endif yara-4.1.3/libyara/include/yara/error.h000066400000000000000000000151211413423160300177540ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_ERROR_H #define YR_ERROR_H #include #if defined(_WIN32) || defined(__CYGWIN__) #include #endif #ifndef ERROR_SUCCESS #define ERROR_SUCCESS 0 #endif // ERROR_INSUFICIENT_MEMORY is misspelled but it's kept for backward // compatibility, as some other programs can be using it in this form. #define ERROR_INSUFICIENT_MEMORY 1 #define ERROR_INSUFFICIENT_MEMORY 1 #define ERROR_COULD_NOT_ATTACH_TO_PROCESS 2 #define ERROR_COULD_NOT_OPEN_FILE 3 #define ERROR_COULD_NOT_MAP_FILE 4 #define ERROR_INVALID_FILE 6 #define ERROR_CORRUPT_FILE 7 #define ERROR_UNSUPPORTED_FILE_VERSION 8 #define ERROR_INVALID_REGULAR_EXPRESSION 9 #define ERROR_INVALID_HEX_STRING 10 #define ERROR_SYNTAX_ERROR 11 #define ERROR_LOOP_NESTING_LIMIT_EXCEEDED 12 #define ERROR_DUPLICATED_LOOP_IDENTIFIER 13 #define ERROR_DUPLICATED_IDENTIFIER 14 #define ERROR_DUPLICATED_TAG_IDENTIFIER 15 #define ERROR_DUPLICATED_META_IDENTIFIER 16 #define ERROR_DUPLICATED_STRING_IDENTIFIER 17 #define ERROR_UNREFERENCED_STRING 18 #define ERROR_UNDEFINED_STRING 19 #define ERROR_UNDEFINED_IDENTIFIER 20 #define ERROR_MISPLACED_ANONYMOUS_STRING 21 #define ERROR_INCLUDES_CIRCULAR_REFERENCE 22 #define ERROR_INCLUDE_DEPTH_EXCEEDED 23 #define ERROR_WRONG_TYPE 24 #define ERROR_EXEC_STACK_OVERFLOW 25 #define ERROR_SCAN_TIMEOUT 26 #define ERROR_TOO_MANY_SCAN_THREADS 27 #define ERROR_CALLBACK_ERROR 28 #define ERROR_INVALID_ARGUMENT 29 #define ERROR_TOO_MANY_MATCHES 30 #define ERROR_INTERNAL_FATAL_ERROR 31 #define ERROR_NESTED_FOR_OF_LOOP 32 #define ERROR_INVALID_FIELD_NAME 33 #define ERROR_UNKNOWN_MODULE 34 #define ERROR_NOT_A_STRUCTURE 35 #define ERROR_NOT_INDEXABLE 36 #define ERROR_NOT_A_FUNCTION 37 #define ERROR_INVALID_FORMAT 38 #define ERROR_TOO_MANY_ARGUMENTS 39 #define ERROR_WRONG_ARGUMENTS 40 #define ERROR_WRONG_RETURN_TYPE 41 #define ERROR_DUPLICATED_STRUCTURE_MEMBER 42 #define ERROR_EMPTY_STRING 43 #define ERROR_DIVISION_BY_ZERO 44 #define ERROR_REGULAR_EXPRESSION_TOO_LARGE 45 #define ERROR_TOO_MANY_RE_FIBERS 46 #define ERROR_COULD_NOT_READ_PROCESS_MEMORY 47 #define ERROR_INVALID_EXTERNAL_VARIABLE_TYPE 48 #define ERROR_REGULAR_EXPRESSION_TOO_COMPLEX 49 #define ERROR_INVALID_MODULE_NAME 50 #define ERROR_TOO_MANY_STRINGS 51 #define ERROR_INTEGER_OVERFLOW 52 #define ERROR_CALLBACK_REQUIRED 53 #define ERROR_INVALID_OPERAND 54 #define ERROR_COULD_NOT_READ_FILE 55 #define ERROR_DUPLICATED_EXTERNAL_VARIABLE 56 #define ERROR_INVALID_MODULE_DATA 57 #define ERROR_WRITING_FILE 58 #define ERROR_INVALID_MODIFIER 59 #define ERROR_DUPLICATED_MODIFIER 60 #define ERROR_BLOCK_NOT_READY 61 #define GOTO_EXIT_ON_ERROR(x) \ { \ result = (x); \ if (result != ERROR_SUCCESS) \ goto _exit; \ } #define FAIL_ON_ERROR(x) \ { \ int result = (x); \ if (result != ERROR_SUCCESS) \ return result; \ } #define GOTO_EXIT_ON_ERROR_WITH_CLEANUP(x, cleanup) \ { \ result = (x); \ if (result != ERROR_SUCCESS) \ { \ cleanup; \ goto _exit; \ } \ } #define FAIL_ON_ERROR_WITH_CLEANUP(x, cleanup) \ { \ int result = (x); \ if (result != ERROR_SUCCESS) \ { \ cleanup; \ return result; \ } \ } #define FAIL_ON_NULL_WITH_CLEANUP(x, cleanup) \ { \ if ((x) == NULL) \ { \ cleanup; \ return ERROR_INSUFFICIENT_MEMORY; \ } \ } #ifdef NDEBUG #define assertf(expr, msg, ...) ((void) 0) #else #define assertf(expr, msg, ...) \ if (!(expr)) \ { \ fprintf(stderr, "%s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ abort(); \ } #endif #endif yara-4.1.3/libyara/include/yara/exec.h000066400000000000000000000153411413423160300175530ustar00rootroot00000000000000/* Copyright (c) 2013-2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_EXEC_H #define YR_EXEC_H #include #include #include #include #define YR_UNDEFINED 0xFFFABADAFABADAFFLL #define IS_UNDEFINED(x) ((size_t)(x) == (size_t) YR_UNDEFINED) #define OP_ERROR 0 #define OP_HALT 255 #define OP_NOP 254 #define OP_AND 1 #define OP_OR 2 #define OP_NOT 3 #define OP_BITWISE_NOT 4 #define OP_BITWISE_AND 5 #define OP_BITWISE_OR 6 #define OP_BITWISE_XOR 7 #define OP_SHL 8 #define OP_SHR 9 #define OP_MOD 10 #define OP_INT_TO_DBL 11 #define OP_STR_TO_BOOL 12 #define OP_PUSH 13 #define OP_POP 14 #define OP_CALL 15 #define OP_OBJ_LOAD 16 #define OP_OBJ_VALUE 17 #define OP_OBJ_FIELD 18 #define OP_INDEX_ARRAY 19 #define OP_COUNT 20 #define OP_LENGTH 21 #define OP_FOUND 22 #define OP_FOUND_AT 23 #define OP_FOUND_IN 24 #define OP_OFFSET 25 #define OP_OF 26 #define OP_PUSH_RULE 27 #define OP_INIT_RULE 28 #define OP_MATCH_RULE 29 #define OP_INCR_M 30 #define OP_CLEAR_M 31 #define OP_ADD_M 32 #define OP_POP_M 33 #define OP_PUSH_M 34 #define OP_SET_M 35 #define OP_SWAPUNDEF 36 #define OP_FILESIZE 37 #define OP_ENTRYPOINT 38 #define OP_UNUSED 39 #define OP_MATCHES 40 #define OP_IMPORT 41 #define OP_LOOKUP_DICT 42 #define OP_JUNDEF 43 /* Not used */ #define OP_JUNDEF_P 44 #define OP_JNUNDEF 45 #define OP_JNUNDEF_P 46 /* Not used */ #define OP_JFALSE 47 #define OP_JFALSE_P 48 #define OP_JTRUE 49 #define OP_JTRUE_P 50 #define OP_JL_P 51 #define OP_JLE_P 52 #define OP_ITER_NEXT 53 #define OP_ITER_START_ARRAY 54 #define OP_ITER_START_DICT 55 #define OP_ITER_START_INT_RANGE 56 #define OP_ITER_START_INT_ENUM 57 #define OP_JZ 58 #define OP_JZ_P 59 #define OP_PUSH_8 60 #define OP_PUSH_16 61 #define OP_PUSH_32 62 #define OP_PUSH_U 63 #define OP_CONTAINS 64 #define OP_STARTSWITH 65 #define OP_ENDSWITH 66 #define OP_ICONTAINS 67 #define OP_ISTARTSWITH 68 #define OP_IENDSWITH 69 #define _OP_EQ 0 #define _OP_NEQ 1 #define _OP_LT 2 #define _OP_GT 3 #define _OP_LE 4 #define _OP_GE 5 #define _OP_ADD 6 #define _OP_SUB 7 #define _OP_MUL 8 #define _OP_DIV 9 #define _OP_MINUS 10 #define OP_INT_BEGIN 100 #define OP_INT_EQ (OP_INT_BEGIN + _OP_EQ) #define OP_INT_NEQ (OP_INT_BEGIN + _OP_NEQ) #define OP_INT_LT (OP_INT_BEGIN + _OP_LT) #define OP_INT_GT (OP_INT_BEGIN + _OP_GT) #define OP_INT_LE (OP_INT_BEGIN + _OP_LE) #define OP_INT_GE (OP_INT_BEGIN + _OP_GE) #define OP_INT_ADD (OP_INT_BEGIN + _OP_ADD) #define OP_INT_SUB (OP_INT_BEGIN + _OP_SUB) #define OP_INT_MUL (OP_INT_BEGIN + _OP_MUL) #define OP_INT_DIV (OP_INT_BEGIN + _OP_DIV) #define OP_INT_MINUS (OP_INT_BEGIN + _OP_MINUS) #define OP_INT_END OP_INT_MINUS #define OP_DBL_BEGIN 120 #define OP_DBL_EQ (OP_DBL_BEGIN + _OP_EQ) #define OP_DBL_NEQ (OP_DBL_BEGIN + _OP_NEQ) #define OP_DBL_LT (OP_DBL_BEGIN + _OP_LT) #define OP_DBL_GT (OP_DBL_BEGIN + _OP_GT) #define OP_DBL_LE (OP_DBL_BEGIN + _OP_LE) #define OP_DBL_GE (OP_DBL_BEGIN + _OP_GE) #define OP_DBL_ADD (OP_DBL_BEGIN + _OP_ADD) #define OP_DBL_SUB (OP_DBL_BEGIN + _OP_SUB) #define OP_DBL_MUL (OP_DBL_BEGIN + _OP_MUL) #define OP_DBL_DIV (OP_DBL_BEGIN + _OP_DIV) #define OP_DBL_MINUS (OP_DBL_BEGIN + _OP_MINUS) #define OP_DBL_END OP_DBL_MINUS #define OP_STR_BEGIN 140 #define OP_STR_EQ (OP_STR_BEGIN + _OP_EQ) #define OP_STR_NEQ (OP_STR_BEGIN + _OP_NEQ) #define OP_STR_LT (OP_STR_BEGIN + _OP_LT) #define OP_STR_GT (OP_STR_BEGIN + _OP_GT) #define OP_STR_LE (OP_STR_BEGIN + _OP_LE) #define OP_STR_GE (OP_STR_BEGIN + _OP_GE) #define OP_STR_END OP_STR_GE #define IS_INT_OP(x) ((x) >= OP_INT_BEGIN && (x) <= OP_INT_END) #define IS_DBL_OP(x) ((x) >= OP_DBL_BEGIN && (x) <= OP_DBL_END) #define IS_STR_OP(x) ((x) >= OP_STR_BEGIN && (x) <= OP_STR_END) #define OP_READ_INT 240 #define OP_INT8 (OP_READ_INT + 0) #define OP_INT16 (OP_READ_INT + 1) #define OP_INT32 (OP_READ_INT + 2) #define OP_UINT8 (OP_READ_INT + 3) #define OP_UINT16 (OP_READ_INT + 4) #define OP_UINT32 (OP_READ_INT + 5) #define OP_INT8BE (OP_READ_INT + 6) #define OP_INT16BE (OP_READ_INT + 7) #define OP_INT32BE (OP_READ_INT + 8) #define OP_UINT8BE (OP_READ_INT + 9) #define OP_UINT16BE (OP_READ_INT + 10) #define OP_UINT32BE (OP_READ_INT + 11) #define OPERATION(operator, op1, op2) \ (IS_UNDEFINED(op1) || IS_UNDEFINED(op2)) ? (YR_UNDEFINED) : (op1 operator op2) #define COMPARISON(operator, op1, op2) \ (IS_UNDEFINED(op1) || IS_UNDEFINED(op2)) ? (0) : (op1 operator op2) int yr_execute_code(YR_SCAN_CONTEXT* context); #endif yara-4.1.3/libyara/include/yara/exefiles.h000066400000000000000000000033241413423160300204310ustar00rootroot00000000000000/* Copyright (c) 2007. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_EXEFILES_H #define YR_EXEFILES_H uint64_t yr_get_entry_point_offset(const uint8_t* buffer, size_t buffer_length); uint64_t yr_get_entry_point_address( const uint8_t* buffer, size_t buffer_length, uint64_t base_address); #endif yara-4.1.3/libyara/include/yara/filemap.h000066400000000000000000000046231413423160300202450ustar00rootroot00000000000000/* Copyright (c) 2007-2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_FILEMAP_H #define YR_FILEMAP_H #include #if defined(_WIN32) || defined(__CYGWIN__) #include #define YR_FILE_DESCRIPTOR HANDLE #else #define YR_FILE_DESCRIPTOR int #endif #include #include #include typedef struct _YR_MAPPED_FILE { YR_FILE_DESCRIPTOR file; size_t size; const uint8_t* data; #if defined(_WIN32) || defined(__CYGWIN__) HANDLE mapping; #endif } YR_MAPPED_FILE; YR_API int yr_filemap_map(const char* file_path, YR_MAPPED_FILE* pmapped_file); YR_API int yr_filemap_map_fd( YR_FILE_DESCRIPTOR file, uint64_t offset, size_t size, YR_MAPPED_FILE* pmapped_file); YR_API int yr_filemap_map_ex( const char* file_path, uint64_t offset, size_t size, YR_MAPPED_FILE* pmapped_file); YR_API void yr_filemap_unmap(YR_MAPPED_FILE* pmapped_file); YR_API void yr_filemap_unmap_fd(YR_MAPPED_FILE* pmapped_file); #endif yara-4.1.3/libyara/include/yara/globals.h000066400000000000000000000107671413423160300202610ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_GLOBALS_H #define YR_GLOBALS_H #include #include // Pre-computed tables for quickly converting a character to lowercase or to // its alternative case (uppercase if it is a lowercase and vice versa). This // tables are initialized by yr_initialize. extern uint8_t yr_lowercase[256]; extern uint8_t yr_altercase[256]; // Thread-local storage (TLS) key used by the regexp and hex string parsers. // Each thread calling yr_parse_re_string/yr_parse_hex_string stores a pointer // to a jmp_buf struct used by setjmp/longjmp for recovering when a fatal error // occurs in the parser. extern YR_THREAD_STORAGE_KEY yr_yyfatal_trampoline_tls; // Thread-local storage (TLS) key used by YR_TRYCATCH. extern YR_THREAD_STORAGE_KEY yr_trycatch_trampoline_tls; // When YARA is built with YR_DEBUG_VERBOSITY defined as larger than 0 it can // print debug information to stdout. #if 0 == YR_DEBUG_VERBOSITY #define YR_DEBUG_INITIALIZE() #define YR_DEBUG_FPRINTF(VERBOSITY, FORMAT, ...) #else // for getpid() #include #include extern double yr_debug_get_elapsed_seconds(void); extern char* yr_debug_callback_message_as_string(int message); extern char* yr_debug_error_as_string(int error); // Default is 0 for production, which means be silent, else verbose. extern uint64_t yr_debug_verbosity; extern YR_TLS int yr_debug_indent; extern const char yr_debug_spaces[]; extern size_t yr_debug_spaces_len; #define YR_DEBUG_INITIALIZE() \ yr_debug_verbosity = getenv("YR_DEBUG_VERBOSITY") \ ? atoi(getenv("YR_DEBUG_VERBOSITY")) \ : YR_DEBUG_VERBOSITY #define YR_DEBUG_FPRINTF(VERBOSITY, STREAM, FORMAT, ...) \ if (yr_debug_verbosity >= VERBOSITY) \ { \ if (FORMAT[0] == '}') \ { \ yr_debug_indent--; \ } \ assert((2 * yr_debug_indent) >= 0); \ assert((2 * yr_debug_indent) < (yr_debug_spaces_len - 2)); \ fprintf( \ STREAM, \ "%f %06u %.*s", \ yr_debug_get_elapsed_seconds(), \ getpid(), \ (2 * yr_debug_indent), \ yr_debug_spaces); \ fprintf(STREAM, FORMAT, __VA_ARGS__); \ if (FORMAT[0] == '+') \ { \ yr_debug_indent++; \ } \ } #endif #endif yara-4.1.3/libyara/include/yara/hash.h000066400000000000000000000067741413423160300175640ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_HASH_H #define YR_HASH_H #include #include #include typedef struct _YR_HASH_TABLE_ENTRY { void* key; size_t key_length; char* ns; void* value; struct _YR_HASH_TABLE_ENTRY* next; } YR_HASH_TABLE_ENTRY; typedef struct _YR_HASH_TABLE { int size; YR_HASH_TABLE_ENTRY* buckets[1]; } YR_HASH_TABLE; typedef int (*YR_HASH_TABLE_FREE_VALUE_FUNC)(void* value); uint32_t yr_hash(uint32_t seed, const void* buffer, size_t len); YR_API int yr_hash_table_create(int size, YR_HASH_TABLE** table); YR_API void yr_hash_table_clean( YR_HASH_TABLE* table, YR_HASH_TABLE_FREE_VALUE_FUNC free_value); YR_API void yr_hash_table_destroy( YR_HASH_TABLE* table, YR_HASH_TABLE_FREE_VALUE_FUNC free_value); YR_API void* yr_hash_table_lookup( YR_HASH_TABLE* table, const char* key, const char* ns); YR_API void* yr_hash_table_remove( YR_HASH_TABLE* table, const char* key, const char* ns); YR_API int yr_hash_table_add( YR_HASH_TABLE* table, const char* key, const char* ns, void* value); YR_API int yr_hash_table_add_uint32( YR_HASH_TABLE* table, const char* key, const char* ns, uint32_t value); YR_API uint32_t yr_hash_table_lookup_uint32( YR_HASH_TABLE* table, const char* key, const char* ns); YR_API void* yr_hash_table_lookup_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns); YR_API void* yr_hash_table_remove_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns); YR_API int yr_hash_table_add_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns, void* value); YR_API int yr_hash_table_add_uint32_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns, uint32_t value); YR_API uint32_t yr_hash_table_lookup_uint32_raw_key( YR_HASH_TABLE* table, const void* key, size_t key_length, const char* ns); #endif yara-4.1.3/libyara/include/yara/hex_lexer.h000066400000000000000000000064661413423160300206220ustar00rootroot00000000000000/* Copyright (c) 2007. Victor M. Alvarez [plusvic@gmail.com]. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #undef yyparse #undef yylex #undef yyerror #undef yyfatal #undef yychar #undef yydebug #undef yynerrs #undef yyget_extra #undef yyget_lineno #undef YY_FATAL_ERROR #undef YY_DECL #undef LEX_ENV #define yyparse hex_yyparse #define yylex hex_yylex #define yyerror hex_yyerror #define yyfatal hex_yyfatal #define yychar hex_yychar #define yydebug hex_yydebug #define yynerrs hex_yynerrs #define yyget_extra hex_yyget_extra #define yyget_lineno hex_yyget_lineno #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif #define YY_EXTRA_TYPE RE_AST* #define YY_USE_CONST typedef struct _HEX_LEX_ENVIRONMENT { int inside_or; int last_error; char last_error_message[256]; } HEX_LEX_ENVIRONMENT; // The default behavior when a fatal error occurs in the parser is calling // exit(YY_EXIT_FAILURE) for terminating the process. This is not acceptable // for a library, which should return gracefully to the calling program. For // this reason we redefine the YY_FATAL_ERROR macro so that it expands to our // own function instead of the one provided by default. #define YY_FATAL_ERROR(msg) hex_yyfatal(yyscanner, msg) #define LEX_ENV ((HEX_LEX_ENVIRONMENT*) lex_env) #include #define YY_DECL \ int hex_yylex( \ YYSTYPE* yylval_param, yyscan_t yyscanner, HEX_LEX_ENVIRONMENT* lex_env) YY_EXTRA_TYPE yyget_extra(yyscan_t yyscanner); int yylex( YYSTYPE* yylval_param, yyscan_t yyscanner, HEX_LEX_ENVIRONMENT* lex_env); int yyparse(void* yyscanner, HEX_LEX_ENVIRONMENT* lex_env); void yyerror( yyscan_t yyscanner, HEX_LEX_ENVIRONMENT* lex_env, const char* error_message); void yyfatal(yyscan_t yyscanner, const char* error_message); int yr_parse_hex_string( const char* hex_string, RE_AST** re_ast, RE_ERROR* error); yara-4.1.3/libyara/include/yara/integers.h000066400000000000000000000057641413423160300204570ustar00rootroot00000000000000/* Copyright (c) 2007-2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_INTEGERS_H #define YR_INTEGERS_H #if (defined(_MSC_VER) && (_MSC_VER < 1600)) || \ (defined(__BORLANDC__) && (__BORLANDC__ <= 0x0560)) #ifdef __cplusplus extern "C" { #endif // Microsoft Visual Studio C++ before Visual Studio 2010 or earlier versions // of the Borland C++ Builder do not support the (u)int#_t type definitions // but have __int# definitions instead typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #ifdef __cplusplus } #endif #ifndef INT8_MIN #define INT8_MIN (-127i8 - 1) #endif #ifndef INT8_MIN #define INT16_MIN (-32767i16 - 1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647i32 - 1) #endif #ifndef INT64_MIN #define INT64_MIN (-9223372036854775807i64 - 1) #endif #ifndef INT8_MAX #define INT8_MAX 127i8 #endif #ifndef INT16_MAX #define INT16_MAX 32767i16 #endif #ifndef INT32_MAX #define INT32_MAX 2147483647i32 #endif #ifndef INT64_MAX #define INT64_MAX 9223372036854775807i64 #endif #ifndef UINT8_MAX #define UINT8_MAX 0xffui8 #endif #ifndef UINT16_MAX #define UINT16_MAX 0xffffui16 #endif #ifndef UINT32_MAX #define UINT32_MAX 0xffffffffui32 #endif #ifndef UINT64_MAX #define UINT64_MAX 0xffffffffffffffffui64 #endif #else // Other "compilers" and later versions of Microsoft Visual Studio C++ and // Borland C/C++ define the types in #include #endif #endif yara-4.1.3/libyara/include/yara/lexer.h000066400000000000000000000061051413423160300177440ustar00rootroot00000000000000/* Copyright (c) 2007. Victor M. Alvarez [plusvic@gmail.com]. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #undef yyparse #undef yylex #undef yyerror #undef yyfatal #undef yychar #undef yydebug #undef yynerrs #undef yyget_extra #undef yyget_lineno #undef YY_DECL #undef YY_FATAL_ERROR #undef YY_EXTRA_TYPE #define yyparse yara_yyparse #define yylex yara_yylex #define yyerror yara_yyerror #define yyfatal yara_yyfatal #define yywarning yara_yywarning #define yychar yara_yychar #define yydebug yara_yydebug #define yynerrs yara_yynerrs #define yyget_extra yara_yyget_extra #define yyget_lineno yara_yyget_lineno #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif union YYSTYPE; #define YY_DECL \ int yylex( \ union YYSTYPE* yylval_param, yyscan_t yyscanner, YR_COMPILER* compiler) #define YY_FATAL_ERROR(msg) yara_yyfatal(yyscanner, msg) #define YY_EXTRA_TYPE YR_COMPILER* #define YY_USE_CONST int yyget_lineno(yyscan_t yyscanner); int yylex( union YYSTYPE* yylval_param, yyscan_t yyscanner, YR_COMPILER* compiler); int yyparse(void* yyscanner, YR_COMPILER* compiler); void yyerror( yyscan_t yyscanner, YR_COMPILER* compiler, const char* error_message); void yywarning(yyscan_t yyscanner, const char* message_fmt, ...) YR_PRINTF_LIKE(2, 3); void yyfatal(yyscan_t yyscanner, const char* error_message); YY_EXTRA_TYPE yyget_extra(yyscan_t yyscanner); int yr_lex_parse_rules_string(const char* rules_string, YR_COMPILER* compiler); int yr_lex_parse_rules_file(FILE* rules_file, YR_COMPILER* compiler); int yr_lex_parse_rules_fd(YR_FILE_DESCRIPTOR rules_fd, YR_COMPILER* compiler); yara-4.1.3/libyara/include/yara/libyara.h000066400000000000000000000070141413423160300202500ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_LIBYARA_H #define YR_LIBYARA_H #include #define YR_MAJOR_VERSION 4 #define YR_MINOR_VERSION 1 #define YR_MICRO_VERSION 3 #define version_str(s) _version_str(s) #define _version_str(s) #s // Version as a string #define YR_VERSION \ version_str(YR_MAJOR_VERSION) "." version_str( \ YR_MINOR_VERSION) "." version_str(YR_MICRO_VERSION) // Version as a single 4-byte hex number, e.g. 0x030401 == 3.4.1. #define YR_VERSION_HEX \ ((YR_MAJOR_VERSION << 16) | (YR_MINOR_VERSION << 8) | (YR_MICRO_VERSION << 0)) // Turn on paranoid mode by default if not defined otherwise. In paranoid // mode additional checks are performed in order to mitigate the effects of // malicious tampering with compiled rules. Such checks are not necessary // when you can ensure that the compiled rules are executed exactly as they // were generated by YARA, without any further modification. Check issue #891 // (https://github.com/VirusTotal/yara/issues/891) for more context. // // Paranoid mode does not guarantee that it's safe to load compiled rules from // third parties, it only prevents severe security issues. Maliciously crafted // compiled rules can still crash YARA. Loading third-party compiled rules is // *highly* discouraged. If you need to distribute YARA rules in compiled // form you should encapsulate them in some digitally-signed package that // ensure that they haven't been modified by someone else. #if !defined(YR_PARANOID_EXEC) #define YR_PARANOID_EXEC 1 #endif // Enumerated type listing configuration options typedef enum _YR_CONFIG_NAME { YR_CONFIG_STACK_SIZE, YR_CONFIG_MAX_STRINGS_PER_RULE, YR_CONFIG_MAX_MATCH_DATA, YR_CONFIG_LAST // End-of-enum marker, not a configuration } YR_CONFIG_NAME; #define DEFAULT_STACK_SIZE 16384 #define DEFAULT_MAX_STRINGS_PER_RULE 10000 #define DEFAULT_MAX_MATCH_DATA 512 YR_API int yr_initialize(void); YR_API int yr_finalize(void); YR_API int yr_set_configuration(YR_CONFIG_NAME, void*); YR_API int yr_get_configuration(YR_CONFIG_NAME, void*); #endif yara-4.1.3/libyara/include/yara/limits.h000066400000000000000000000140421413423160300201250ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_LIMITS_H #define YR_LIMITS_H #if defined(_WIN32) || defined(__CYGWIN__) #include #endif #include "utils.h" // Maximum length of file paths. This is the only limit that doesn't have the // YR_ prefix. The intention is using the default MAX_PATH if defined. #ifndef MAX_PATH #define MAX_PATH 1024 #endif // Maximum number of threads that can use a YR_RULES structure simultaneously. // Increasing this number also increase memory usage as each YR_STRING structure // has an array with YR_MAX_THREADS entries for storing pointers to YR_MATCH // structures. #ifndef YR_MAX_THREADS #define YR_MAX_THREADS 32 #endif // Maximum number of buffers that an arena can have. #ifndef YR_MAX_ARENA_BUFFERS #define YR_MAX_ARENA_BUFFERS 16 #endif // Capacity of the buffer used for storing compiler error messages. Messages // will be truncated at this size. #ifndef YR_MAX_COMPILER_ERROR_EXTRA_INFO #define YR_MAX_COMPILER_ERROR_EXTRA_INFO 256 #endif // Maximum size for the substring (atoms) extracted from strings and regular // expressions and put into the Aho-Corasick automaton. The maximum allows size // for this constant is 255. #ifndef YR_MAX_ATOM_LENGTH #define YR_MAX_ATOM_LENGTH 4 #endif #ifndef YR_MAX_ATOM_QUALITY #define YR_MAX_ATOM_QUALITY 255 #endif #ifndef YR_MIN_ATOM_QUALITY #define YR_MIN_ATOM_QUALITY 0 #endif // If the minimum atom quality for a string or regexp is below this constant, // a warning like " is slowing down the scan" is shown. This is used // only with heuristic atom quality, when using an atom quality table the user // must specify the threshold when calling yr_compiler_set_atom_quality_table. #ifndef YR_ATOM_QUALITY_WARNING_THRESHOLD #define YR_ATOM_QUALITY_WARNING_THRESHOLD \ YR_MAX_ATOM_QUALITY - 20 * YR_MAX_ATOM_LENGTH + 38 #endif // If a rule generates more than this number of atoms a warning is shown. #ifndef YR_ATOMS_PER_RULE_WARNING_THRESHOLD #define YR_ATOMS_PER_RULE_WARNING_THRESHOLD 10000 #endif // Maximum number of nested "for" loops in rule. Rules ith nested loops // exceeding this number will be rejected by the compiler. #ifndef YR_MAX_LOOP_NESTING #define YR_MAX_LOOP_NESTING 4 #endif // Maximum number of local variables in a "for" loop. This includes the // variables defined explicitly defined by the user, not the internal variables // required for maintaining the loop's state. #ifndef YR_MAX_LOOP_VARS #define YR_MAX_LOOP_VARS 2 #endif // Maximum number of nested included files. #ifndef YR_MAX_INCLUDE_DEPTH #define YR_MAX_INCLUDE_DEPTH 16 #endif // Maximum number of matches allowed for a string. If more matches are found // the scan will have a CALLBACK_MSG_TOO_MANY_MATCHES. #ifndef YR_MAX_STRING_MATCHES #define YR_MAX_STRING_MATCHES 1000000 #endif // Maximum number of argument that a function in a YARA module can have. #ifndef YR_MAX_FUNCTION_ARGS #define YR_MAX_FUNCTION_ARGS 128 #endif // How many overloaded functions can share the same name in a YARA module. #ifndef YR_MAX_OVERLOADED_FUNCTIONS #define YR_MAX_OVERLOADED_FUNCTIONS 10 #endif // Size of the stack used by yr_re_fast_exec. #ifndef YR_MAX_FAST_RE_STACK #define YR_MAX_FAST_RE_STACK 300 #endif // Regular expressions like /foo.{x,y}bar/ are split in two separate ones /foo/ // and /bar/ if x is larger than YR_STRING_CHAINING_THRESHOLD. This also applies // to hex strings like { 01 02 03 [x-y] 004 05 06 }. #ifndef YR_STRING_CHAINING_THRESHOLD #define YR_STRING_CHAINING_THRESHOLD 200 #endif // Size of the buffer used by the lexer for storing strings like include file // paths and regular expressions. #ifndef YR_LEX_BUF_SIZE #define YR_LEX_BUF_SIZE 8192 #endif // Each time an atom is found it means that we have a potential match for some // string, and that match must be verified. The time spent in verifying those // matches is measured in one out of YR_MATCH_VERIFICATION_PROFILING_RATE // matches. The time is not measured for all matches because measuring it is // expensive by itself and match verification is a frequent operation. #ifndef YR_MATCH_VERIFICATION_PROFILING_RATE #define YR_MATCH_VERIFICATION_PROFILING_RATE 1024 #endif // Maximum allowed split ID, also limiting the number of split instructions // allowed in a regular expression. This number can't be increased // over 255 without changing RE_SPLIT_ID_TYPE. #ifndef RE_MAX_SPLIT_ID #define RE_MAX_SPLIT_ID 128 #endif // Maximum stack size for regexp evaluation #ifndef RE_MAX_STACK #define RE_MAX_STACK 1024 #endif // Maximum input size scanned by yr_re_exec #ifndef YR_RE_SCAN_LIMIT #define YR_RE_SCAN_LIMIT 4096 #endif // Maximum number of fibers #ifndef RE_MAX_FIBERS #define RE_MAX_FIBERS 1024 #endif #endif yara-4.1.3/libyara/include/yara/macho.h000066400000000000000000000375501413423160300177240ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _MACHO_H #define _MACHO_H #include // Mach-O file format magic constants #define MH_MAGIC 0xfeedface #define MH_CIGAM 0xcefaedfe #define MH_MAGIC_64 0xfeedfacf #define MH_CIGAM_64 0xcffaedfe // Mach-O universal binary magic constants #define FAT_MAGIC 0xcafebabe #define FAT_CIGAM 0xbebafeca #define FAT_MAGIC_64 0xcafebabf #define FAT_CIGAM_64 0xbfbafeca // Mach-O 64-bit masks #define CPU_ARCH_ABI64 0x01000000 // 64-bit ABI mask (for cputype) #define CPU_SUBTYPE_LIB64 0x80000000 // 64-bit library mask (for cpusubtype) // Mach-O CPU types #define CPU_TYPE_MC680X0 0x00000006 // Motorola 68000 #define CPU_TYPE_I386 0x00000007 // AMD/Intel x86 #define CPU_TYPE_X86 0x00000007 // AMD/Intel x86 #define CPU_TYPE_X86_64 0x01000007 // AMD/Intel x86-64 #define CPU_TYPE_MIPS 0x00000008 // MIPS #define CPU_TYPE_MC98000 0x0000000a // Motorola PowerPC #define CPU_TYPE_HPPA 0x0000000b // HP PA-RISC #define CPU_TYPE_ARM 0x0000000c // ARM #define CPU_TYPE_ARM64 0x0100000c // ARM 64-bit #define CPU_TYPE_MC88000 0x0000000d // Motorola 88000 #define CPU_TYPE_SPARC 0x0000000e // SPARC #define CPU_TYPE_I860 0x0000000f // Intel i860 #define CPU_TYPE_ALPHA 0x00000010 // DEC Alpha #define CPU_TYPE_POWERPC 0x00000012 // PowerPC #define CPU_TYPE_POWERPC64 0x01000012 // PowerPC 64-bit // Mach-O Intel CPU sub-types #define CPU_SUBTYPE_INTEL_MODEL_ALL 0x00 #define CPU_SUBTYPE_386 0x03 #define CPU_SUBTYPE_486 0x04 #define CPU_SUBTYPE_486SX 0x84 #define CPU_SUBTYPE_586 0x05 #define CPU_SUBTYPE_PENT 0x05 #define CPU_SUBTYPE_PENTPRO 0x16 #define CPU_SUBTYPE_PENTII_M3 0x36 #define CPU_SUBTYPE_PENTII_M5 0x56 #define CPU_SUBTYPE_CELERON 0x67 #define CPU_SUBTYPE_CELERON_MOBILE 0x77 #define CPU_SUBTYPE_PENTIUM_3 0x08 #define CPU_SUBTYPE_PENTIUM_3_M 0x18 #define CPU_SUBTYPE_PENTIUM_3_XEON 0x28 #define CPU_SUBTYPE_PENTIUM_M 0x09 #define CPU_SUBTYPE_PENTIUM_4 0x0a #define CPU_SUBTYPE_PENTIUM_4_M 0x1a #define CPU_SUBTYPE_ITANIUM 0x0b #define CPU_SUBTYPE_ITANIUM_2 0x1b #define CPU_SUBTYPE_XEON 0x0c #define CPU_SUBTYPE_XEON_MP 0x1c // Mach-O ARM CPU sub-types #define CPU_SUBTYPE_ARM_ALL 0x00 #define CPU_SUBTYPE_ARM_V4T 0x05 #define CPU_SUBTYPE_ARM_V6 0x06 #define CPU_SUBTYPE_ARM_V5 0x07 #define CPU_SUBTYPE_ARM_V5TEJ 0x07 #define CPU_SUBTYPE_ARM_XSCALE 0x08 #define CPU_SUBTYPE_ARM_V7 0x09 #define CPU_SUBTYPE_ARM_V7F 0x0a #define CPU_SUBTYPE_ARM_V7S 0x0b #define CPU_SUBTYPE_ARM_V7K 0x0c #define CPU_SUBTYPE_ARM_V6M 0x0e #define CPU_SUBTYPE_ARM_V7M 0x0f #define CPU_SUBTYPE_ARM_V7EM 0x10 // Mach-O ARM 64-bit CPU sub-types #define CPU_SUBTYPE_ARM64_ALL 0x00 // Mach-O SPARC CPU sub-types #define CPU_SUBTYPE_SPARC_ALL 0x00 // Mach-O PowerPC CPU sub-types #define CPU_SUBTYPE_POWERPC_ALL 0x00 #define CPU_SUBTYPE_MC980000_ALL 0x00 #define CPU_SUBTYPE_POWERPC_601 0x01 #define CPU_SUBTYPE_MC98601 0x01 #define CPU_SUBTYPE_POWERPC_602 0x02 #define CPU_SUBTYPE_POWERPC_603 0x03 #define CPU_SUBTYPE_POWERPC_603e 0x04 #define CPU_SUBTYPE_POWERPC_603ev 0x05 #define CPU_SUBTYPE_POWERPC_604 0x06 #define CPU_SUBTYPE_POWERPC_604e 0x07 #define CPU_SUBTYPE_POWERPC_620 0x08 #define CPU_SUBTYPE_POWERPC_750 0x09 #define CPU_SUBTYPE_POWERPC_7400 0x0a #define CPU_SUBTYPE_POWERPC_7450 0x0b #define CPU_SUBTYPE_POWERPC_970 0x64 // Mach-O file types #define MH_OBJECT 0x01 // Object file #define MH_EXECUTE 0x02 // Executable file #define MH_FVMLIB 0x03 // Fixed VM shared library #define MH_CORE 0x04 // Core dump file #define MH_PRELOAD 0x05 // Preloaded executable file #define MH_DYLIB 0x06 // Dynamic shared library #define MH_DYLINKER 0x07 // Dynamic linker shared library #define MH_BUNDLE 0x08 // Bundle file #define MH_DYLIB_STUB 0x09 // Dynamic shared library stub #define MH_DSYM 0x0a // Companion debug sections file #define MH_KEXT_BUNDLE 0x0b // Kernel extension // Mach-O file flags #define MH_NOUNDEFS 0x00000001 #define MH_INCRLINK 0x00000002 #define MH_DYLDLINK 0x00000004 #define MH_BINDATLOAD 0x00000008 #define MH_PREBOUND 0x00000010 #define MH_SPLIT_SEGS 0x00000020 #define MH_LAZY_INIT 0x00000040 #define MH_TWOLEVEL 0x00000080 #define MH_FORCE_FLAT 0x00000100 #define MH_NOMULTIDEFS 0x00000200 #define MH_NOFIXPREBINDING 0x00000400 #define MH_PREBINDABLE 0x00000800 #define MH_ALLMODSBOUND 0x00001000 #define MH_SUBSECTIONS_VIA_SYMBOLS 0x00002000 #define MH_CANONICAL 0x00004000 #define MH_WEAK_DEFINES 0x00008000 #define MH_BINDS_TO_WEAK 0x00010000 #define MH_ALLOW_STACK_EXECUTION 0x00020000 #define MH_ROOT_SAFE 0x00040000 #define MH_SETUID_SAFE 0x00080000 #define MH_NO_REEXPORTED_DYLIBS 0x00100000 #define MH_PIE 0x00200000 #define MH_DEAD_STRIPPABLE_DYLIB 0x00400000 #define MH_HAS_TLV_DESCRIPTORS 0x00800000 #define MH_NO_HEAP_EXECUTION 0x01000000 #define MH_APP_EXTENSION_SAFE 0x02000000 // Mach-O load commands #define LC_SEGMENT 0x00000001 #define LC_SYMTAB 0x00000002 #define LC_SYMSEG 0x00000003 #define LC_THREAD 0x00000004 #define LC_UNIXTHREAD 0x00000005 #define LC_LOADFVMLIB 0x00000006 #define LC_IDFVMLIB 0x00000007 #define LC_IDENT 0x00000008 #define LC_FVMFILE 0x00000009 #define LC_PREPAGE 0x0000000a #define LC_DYSYMTAB 0x0000000b #define LC_LOAD_DYLIB 0x0000000c #define LC_ID_DYLIB 0x0000000d #define LC_LOAD_DYLINKER 0x0000000e #define LC_ID_DYLINKER 0x0000000f #define LC_PREBOUND_DYLIB 0x00000010 #define LC_ROUTINES 0x00000011 #define LC_SUB_FRAMEWORK 0x00000012 #define LC_SUB_UMBRELLA 0x00000013 #define LC_SUB_CLIENT 0x00000014 #define LC_SUB_LIBRARY 0x00000015 #define LC_TWOLEVEL_HINTS 0x00000016 #define LC_PREBIND_CKSUM 0x00000017 #define LC_LOAD_WEAK_DYLIB 0x80000018 #define LC_SEGMENT_64 0x00000019 #define LC_ROUTINES_64 0x0000001A #define LC_UUID 0x0000001B #define LC_RPATH 0x8000001C #define LC_CODE_SIGNATURE 0x0000001D #define LC_SEGMENT_SPLIT_INFO 0x0000001E #define LC_REEXPORT_DYLIB 0x8000001F #define LC_LAZY_LOAD_DYLIB 0x00000020 #define LC_ENCRYPTION_INFO 0x00000021 #define LC_DYLD_INFO 0x00000022 #define LC_DYLD_INFO_ONLY 0x80000022 #define LC_LOAD_UPWARD_DYLIB 0x80000023 #define LC_VERSION_MIN_MACOSX 0x00000024 #define LC_VERSION_MIN_IPHONEOS 0x00000025 #define LC_FUNCTION_STARTS 0x00000026 #define LC_DYLD_ENVIRONMENT 0x00000027 #define LC_MAIN 0x80000028 #define LC_DATA_IN_CODE 0x00000029 #define LC_SOURCE_VERSION 0x0000002A #define LC_DYLIB_CODE_SIGN_DRS 0x0000002B #define LC_ENCRYPTION_INFO_64 0x0000002C #define LC_LINKER_OPTION 0x0000002D #define LC_LINKER_OPTIMIZATION_HINT 0x0000002E #define LC_VERSION_MIN_TVOS 0x0000002F #define LC_VERSION_MIN_WATCHOS 0x00000030 // Segment flags #define SG_HIGHVM 0x00000001 // Use high part of VM (stack) #define SG_FVMLIB 0x00000002 // Allocated by a fixed VM library #define SG_NORELOC 0x00000004 // No associated relocations #define SG_PROTECTED_VERSION_1 0x00000008 // Segment is encryption protected // Section flag masks #define SECTION_TYPE 0x000000ff // Section type mask #define SECTION_ATTRIBUTES 0xffffff00 // Section attributes mask // Section type (use SECTION_TYPE mask) #define S_REGULAR 0x00 #define S_ZEROFILL 0x01 #define S_CSTRING_LITERALS 0x02 #define S_4BYTE_LITERALS 0x03 #define S_8BYTE_LITERALS 0x04 #define S_LITERAL_POINTERS 0x05 #define S_NON_LAZY_SYMBOL_POINTERS 0x06 #define S_LAZY_SYMBOL_POINTERS 0x07 #define S_SYMBOL_STUBS 0x08 #define S_MOD_INIT_FUNC_POINTERS 0x09 #define S_MOD_TERM_FUNC_POINTERS 0x0a #define S_COALESCED 0x0b #define S_GB_ZEROFILL 0x0c #define S_INTERPOSING 0x0d #define S_16BYTE_LITERALS 0x0e #define S_DTRACE_DOF 0x0f #define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 #define S_THREAD_LOCAL_REGULAR 0x11 #define S_THREAD_LOCAL_ZEROFILL 0x12 #define S_THREAD_LOCAL_VARIABLES 0x13 #define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 #define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 // Section attributes (use SECTION_ATTRIBUTES mask) #define S_ATTR_PURE_INSTRUCTIONS 0x80000000 // Only pure instructions #define S_ATTR_NO_TOC 0x40000000 // Contains coalesced symbols #define S_ATTR_STRIP_STATIC_SYMS 0x20000000 // Can strip static symbols #define S_ATTR_NO_DEAD_STRIP 0x10000000 // No dead stripping #define S_ATTR_LIVE_SUPPORT 0x08000000 // Live blocks support #define S_ATTR_SELF_MODIFYING_CODE 0x04000000 // Self modifying code #define S_ATTR_DEBUG 0x02000000 // Debug section #define S_ATTR_SOME_INSTRUCTIONS 0x00000400 // Some machine instructions #define S_ATTR_EXT_RELOC 0x00000200 // Has external relocations #define S_ATTR_LOC_RELOC 0x00000100 // Has local relocations #pragma pack(push, 1) typedef struct { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; } yr_mach_header_32_t; typedef struct { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; uint32_t reserved; } yr_mach_header_64_t; typedef struct { uint32_t cmd; uint32_t cmdsize; } yr_load_command_t; typedef struct { uint32_t cmd; uint32_t cmdsize; char segname[16]; uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; uint32_t filesize; uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; } yr_segment_command_32_t; typedef struct { uint32_t cmd; uint32_t cmdsize; char segname[16]; uint64_t vmaddr; uint64_t vmsize; uint64_t fileoff; uint64_t filesize; uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; } yr_segment_command_64_t; typedef struct { char sectname[16]; char segname[16]; uint32_t addr; uint32_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; } yr_section_32_t; typedef struct { char sectname[16]; char segname[16]; uint64_t addr; uint64_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; uint32_t reserved3; } yr_section_64_t; typedef struct { uint32_t cmd; uint32_t cmdsize; uint8_t uuid[16]; } yr_uuid_command_t; typedef struct { uint32_t cmd; uint32_t cmdsize; uint64_t entryoff; uint64_t stacksize; } yr_entry_point_command_t; typedef struct { uint32_t cmd; uint32_t cmdsize; uint32_t flavor; uint32_t count; // cpu_thread_state } yr_thread_command_t; typedef struct { uint32_t eax; uint32_t ebx; uint32_t ecx; uint32_t edx; uint32_t edi; uint32_t esi; uint32_t ebp; uint32_t esp; uint32_t ss; uint32_t eflags; uint32_t eip; uint32_t cs; uint32_t ds; uint32_t es; uint32_t fs; uint32_t gs; } yr_x86_thread_state_t; typedef struct { uint32_t r[13]; uint32_t sp; uint32_t lr; uint32_t pc; uint32_t cpsr; } yr_arm_thread_state_t; typedef struct { uint32_t srr0; uint32_t srr1; uint32_t r[32]; uint32_t cr; uint32_t xer; uint32_t lr; uint32_t ctr; uint32_t mq; uint32_t vrsavead; } yr_ppc_thread_state_t; typedef struct { uint32_t psr; uint32_t pc; uint32_t npc; uint32_t y; uint32_t g1; uint32_t g2; uint32_t g3; uint32_t g4; uint32_t g5; uint32_t g6; uint32_t g7; uint32_t o0; uint32_t o1; uint32_t o2; uint32_t o3; uint32_t o4; uint32_t o5; uint32_t o6; uint32_t o7; } yr_sparc_thread_state_t; typedef struct { uint32_t dreg[8]; uint32_t areg[8]; uint16_t pad; uint16_t sr; uint32_t pc; } yr_m68k_thread_state_t; typedef struct { uint32_t r1; uint32_t r2; uint32_t r3; uint32_t r4; uint32_t r5; uint32_t r6; uint32_t r7; uint32_t r8; uint32_t r9; uint32_t r10; uint32_t r11; uint32_t r12; uint32_t r13; uint32_t r14; uint32_t r15; uint32_t r16; uint32_t r17; uint32_t r18; uint32_t r19; uint32_t r20; uint32_t r21; uint32_t r22; uint32_t r23; uint32_t r24; uint32_t r25; uint32_t r26; uint32_t r27; uint32_t r28; uint32_t r29; uint32_t r30; uint32_t r31; uint32_t xip; uint32_t xip_in_bd; uint32_t nip; } yr_m88k_thread_state_t; typedef struct { uint64_t rax; uint64_t rbx; uint64_t rcx; uint64_t rdx; uint64_t rdi; uint64_t rsi; uint64_t rbp; uint64_t rsp; uint64_t r8; uint64_t r9; uint64_t r10; uint64_t r11; uint64_t r12; uint64_t r13; uint64_t r14; uint64_t r15; uint64_t rip; uint64_t rflags; uint64_t cs; uint64_t fs; uint64_t gs; } yr_x86_thread_state64_t; typedef struct { uint64_t r[29]; uint64_t fp; uint64_t lr; uint64_t sp; uint64_t pc; uint64_t cpsr; } yr_arm_thread_state64_t; typedef struct { uint64_t srr0; uint64_t srr1; uint64_t r[32]; uint32_t cr; uint64_t xer; uint64_t lr; uint64_t ctr; uint32_t vrsave; } yr_ppc_thread_state64_t; typedef struct { uint32_t magic; uint32_t nfat_arch; } yr_fat_header_t; typedef struct { uint32_t cputype; uint32_t cpusubtype; uint32_t offset; uint32_t size; uint32_t align; } yr_fat_arch_32_t; typedef struct { uint32_t cputype; uint32_t cpusubtype; uint64_t offset; uint64_t size; uint32_t align; uint32_t reserved; } yr_fat_arch_64_t; #pragma pack(pop) #endif yara-4.1.3/libyara/include/yara/mem.h000066400000000000000000000037741413423160300174140ustar00rootroot00000000000000/* Copyright (c) 2007. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_MEM_H #define YR_MEM_H #include #ifdef DMALLOC #define yr_malloc malloc #define yr_calloc calloc #define yr_realloc realloc #define yr_free free #define yr_strdup strdup #define yr_strndup strndup #include #else void* yr_calloc(size_t count, size_t size); void* yr_malloc(size_t size); void* yr_realloc(void* ptr, size_t size); void yr_free(void* ptr); char* yr_strdup(const char* str); char* yr_strndup(const char* str, size_t n); #endif int yr_heap_alloc(void); int yr_heap_free(void); #endif yara-4.1.3/libyara/include/yara/modules.h000066400000000000000000000343331413423160300203010ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_MODULES_H #define YR_MODULES_H #include #include #include #include #include #include #include #include #include #include #include #include // Concatenation that macro-expands its arguments. #define YR_CONCAT(arg1, arg2) _YR_CONCAT(arg1, arg2) // expands the arguments. #define _YR_CONCAT(arg1, arg2) arg1##arg2 // do the actual concatenation. #define module_declarations YR_CONCAT(MODULE_NAME, __declarations) #define module_load YR_CONCAT(MODULE_NAME, __load) #define module_unload YR_CONCAT(MODULE_NAME, __unload) #define module_initialize YR_CONCAT(MODULE_NAME, __initialize) #define module_finalize YR_CONCAT(MODULE_NAME, __finalize) #define begin_declarations \ int module_declarations(YR_OBJECT* module) \ { \ YR_OBJECT* stack[64]; \ int stack_top = 0; \ stack[stack_top] = module; #define end_declarations \ return ERROR_SUCCESS; \ } #define begin_struct(name) \ { \ YR_OBJECT* structure; \ FAIL_ON_ERROR(yr_object_create( \ OBJECT_TYPE_STRUCTURE, name, stack[stack_top], &structure)); \ assertf( \ stack_top < sizeof(stack) / sizeof(stack[0]) - 1, \ "too many nested structures"); \ stack[++stack_top] = structure; \ } #define begin_struct_array(name) \ { \ YR_OBJECT* structure; \ YR_OBJECT* array; \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_ARRAY, name, stack[stack_top], &array)); \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_STRUCTURE, name, array, &structure)); \ assertf( \ stack_top < sizeof(stack) / sizeof(stack[0]) - 1, \ "too many nested structures"); \ stack[++stack_top] = structure; \ } #define begin_struct_dictionary(name) \ { \ YR_OBJECT* structure; \ YR_OBJECT* array; \ FAIL_ON_ERROR(yr_object_create( \ OBJECT_TYPE_DICTIONARY, name, stack[stack_top], &array)); \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_STRUCTURE, name, array, &structure)); \ assertf( \ stack_top < sizeof(stack) / sizeof(stack[0]) - 1, \ "too many nested structures"); \ stack[++stack_top] = structure; \ } #define end_struct(name) \ { \ assert(stack[stack_top]->type == OBJECT_TYPE_STRUCTURE); \ assertf( \ strcmp(stack[stack_top]->identifier, name) == 0, \ "unbalanced begin_struct/end_struct"); \ stack_top--; \ } #define end_struct_array(name) \ end_struct \ (name) #define end_struct_dictionary(name) \ end_struct \ (name) #define declare_integer(name) \ { \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_INTEGER, name, stack[stack_top], NULL)); \ } #define declare_integer_array(name) \ { \ YR_OBJECT* array; \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_ARRAY, name, stack[stack_top], &array)); \ FAIL_ON_ERROR(yr_object_create(OBJECT_TYPE_INTEGER, name, array, NULL)); \ } #define declare_integer_dictionary(name) \ { \ YR_OBJECT* dict; \ FAIL_ON_ERROR(yr_object_create( \ OBJECT_TYPE_DICTIONARY, name, stack[stack_top], &dict)); \ FAIL_ON_ERROR(yr_object_create(OBJECT_TYPE_INTEGER, name, dict, NULL)); \ } #define declare_float(name) \ { \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_FLOAT, name, stack[stack_top], NULL)); \ } #define declare_float_array(name) \ { \ YR_OBJECT* array; \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_ARRAY, name, stack[stack_top], &array)); \ FAIL_ON_ERROR(yr_object_create(OBJECT_TYPE_FLOAT, name, array, NULL)); \ } #define declare_float_dictionary(name) \ { \ YR_OBJECT* dict; \ FAIL_ON_ERROR(yr_object_create( \ OBJECT_TYPE_DICTIONARY, name, stack[stack_top], &dict)); \ FAIL_ON_ERROR(yr_object_create(OBJECT_TYPE_FLOAT, name, dict, NULL)); \ } #define declare_string(name) \ { \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_STRING, name, stack[stack_top], NULL)); \ } #define declare_string_array(name) \ { \ YR_OBJECT* array; \ FAIL_ON_ERROR( \ yr_object_create(OBJECT_TYPE_ARRAY, name, stack[stack_top], &array)); \ FAIL_ON_ERROR(yr_object_create(OBJECT_TYPE_STRING, name, array, NULL)); \ } #define declare_string_dictionary(name) \ { \ YR_OBJECT* dict; \ FAIL_ON_ERROR(yr_object_create( \ OBJECT_TYPE_DICTIONARY, name, stack[stack_top], &dict)); \ FAIL_ON_ERROR(yr_object_create(OBJECT_TYPE_STRING, name, dict, NULL)); \ } #define declare_function(name, args_fmt, ret_fmt, func) \ { \ YR_OBJECT* function; \ FAIL_ON_ERROR(yr_object_function_create( \ name, args_fmt, ret_fmt, func, stack[stack_top], &function)); \ } #define define_function(func) \ int func( \ YR_VALUE* __args, \ YR_SCAN_CONTEXT* __context, \ YR_OBJECT_FUNCTION* __function_obj) #define sized_string_argument(n) (__args[n - 1].ss) #define string_argument(n) (sized_string_argument(n)->c_string) #define integer_argument(n) (__args[n - 1].i) #define float_argument(n) (__args[n - 1].d) #define regexp_argument(n) ((RE*) (__args[n - 1].re)) #define module() yr_object_get_root((YR_OBJECT*) __function_obj) #define parent() (__function_obj->parent) #define scan_context() (__context) #define foreach_memory_block(iterator, block) \ for (block = iterator->first(iterator); block != NULL; \ block = iterator->next(iterator)) #define first_memory_block(context) \ (context)->iterator->first((context)->iterator) #define is_undefined(object, ...) \ yr_object_has_undefined_value(object, __VA_ARGS__) #define get_object(object, ...) yr_object_lookup(object, 0, __VA_ARGS__) #define get_integer(object, ...) yr_object_get_integer(object, __VA_ARGS__) #define get_float(object, ...) yr_object_get_float(object, __VA_ARGS__) #define get_string(object, ...) yr_object_get_string(object, __VA_ARGS__) #define set_integer(value, object, ...) \ yr_object_set_integer(value, object, __VA_ARGS__) #define set_float(value, object, ...) \ yr_object_set_float(value, object, __VA_ARGS__) #define set_sized_string(value, len, object, ...) \ yr_object_set_string(value, len, object, __VA_ARGS__) #define set_string(value, object, ...) \ set_sized_string( \ value, (value == NULL) ? 0 : strlen(value), object, __VA_ARGS__) #define return_integer(integer) \ { \ assertf( \ __function_obj->return_obj->type == OBJECT_TYPE_INTEGER, \ "return type differs from function declaration"); \ return yr_object_set_integer((integer), __function_obj->return_obj, NULL); \ } #define return_float(double_) \ { \ double d = (double) (double_); \ assertf( \ __function_obj->return_obj->type == OBJECT_TYPE_FLOAT, \ "return type differs from function declaration"); \ return yr_object_set_float( \ (d != (double) YR_UNDEFINED) ? d : NAN, \ __function_obj->return_obj, \ NULL); \ } #define return_string(string) \ { \ char* s = (char*) (string); \ assertf( \ __function_obj->return_obj->type == OBJECT_TYPE_STRING, \ "return type differs from function declaration"); \ return yr_object_set_string( \ (s != (char*) YR_UNDEFINED) ? s : NULL, \ (s != (char*) YR_UNDEFINED) ? strlen(s) : 0, \ __function_obj->return_obj, \ NULL); \ } typedef int (*YR_EXT_INITIALIZE_FUNC)(YR_MODULE* module); typedef int (*YR_EXT_FINALIZE_FUNC)(YR_MODULE* module); typedef int (*YR_EXT_DECLARATIONS_FUNC)(YR_OBJECT* module_object); typedef int (*YR_EXT_LOAD_FUNC)( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size); typedef int (*YR_EXT_UNLOAD_FUNC)(YR_OBJECT* module_object); struct YR_MODULE { char* name; YR_EXT_DECLARATIONS_FUNC declarations; YR_EXT_LOAD_FUNC load; YR_EXT_UNLOAD_FUNC unload; YR_EXT_INITIALIZE_FUNC initialize; YR_EXT_FINALIZE_FUNC finalize; }; struct YR_MODULE_IMPORT { const char* module_name; void* module_data; size_t module_data_size; }; int yr_modules_initialize(void); int yr_modules_finalize(void); int yr_modules_do_declarations( const char* module_name, YR_OBJECT* main_structure); int yr_modules_load(const char* module_name, YR_SCAN_CONTEXT* context); int yr_modules_unload_all(YR_SCAN_CONTEXT* context); #endif yara-4.1.3/libyara/include/yara/notebook.h000066400000000000000000000005441413423160300204460ustar00rootroot00000000000000// // Created by Victor Manuel Alvarez on 3/4/20. // #ifndef YR_NOTEBOOK_H #define YR_NOTEBOOK_H #include typedef struct YR_NOTEBOOK YR_NOTEBOOK; int yr_notebook_create(size_t page_size, YR_NOTEBOOK** pool); int yr_notebook_destroy(YR_NOTEBOOK* pool); void* yr_notebook_alloc(YR_NOTEBOOK* notebook, size_t size); #endif // YR_NOTEBOOK_H yara-4.1.3/libyara/include/yara/object.h000066400000000000000000000102471413423160300200750ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_OBJECT_H #define YR_OBJECT_H #ifdef _MSC_VER #include #ifndef isnan #define isnan _isnan #endif #ifndef INFINITY #define INFINITY (DBL_MAX + DBL_MAX) #endif #ifndef NAN #define NAN (INFINITY - INFINITY) #endif #endif #include #include #include #define OBJECT_CREATE 1 #define OBJECT_TYPE_INTEGER 1 #define OBJECT_TYPE_STRING 2 #define OBJECT_TYPE_STRUCTURE 3 #define OBJECT_TYPE_ARRAY 4 #define OBJECT_TYPE_FUNCTION 5 #define OBJECT_TYPE_DICTIONARY 6 #define OBJECT_TYPE_FLOAT 7 int yr_object_create( int8_t type, const char* identifier, YR_OBJECT* parent, YR_OBJECT** object); void yr_object_set_canary(YR_OBJECT* object, int canary); int yr_object_function_create( const char* identifier, const char* arguments_fmt, const char* return_fmt, YR_MODULE_FUNC func, YR_OBJECT* parent, YR_OBJECT** function); int yr_object_from_external_variable( YR_EXTERNAL_VARIABLE* external, YR_OBJECT** object); void yr_object_destroy(YR_OBJECT* object); int yr_object_copy(YR_OBJECT* object, YR_OBJECT** object_copy); YR_OBJECT* yr_object_lookup_field(YR_OBJECT* object, const char* field_name); YR_OBJECT* yr_object_lookup( YR_OBJECT* root, int flags, const char* pattern, ...) YR_PRINTF_LIKE(3, 4); bool yr_object_has_undefined_value(YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(2, 3); double yr_object_get_float(YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(2, 3); int64_t yr_object_get_integer(YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(2, 3); SIZED_STRING* yr_object_get_string(YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(2, 3); int yr_object_set_integer( int64_t value, YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(3, 4); int yr_object_set_float(double value, YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(3, 4); int yr_object_set_string( const char* value, size_t len, YR_OBJECT* object, const char* field, ...) YR_PRINTF_LIKE(4, 5); int yr_object_array_length(YR_OBJECT* object); YR_OBJECT* yr_object_array_get_item(YR_OBJECT* object, int flags, int index); int yr_object_array_set_item(YR_OBJECT* object, YR_OBJECT* item, int index); YR_OBJECT* yr_object_dict_get_item( YR_OBJECT* object, int flags, const char* key); int yr_object_dict_set_item( YR_OBJECT* object, YR_OBJECT* item, const char* key); int yr_object_structure_set_member(YR_OBJECT* object, YR_OBJECT* member); YR_OBJECT* yr_object_get_root(YR_OBJECT* object); YR_API void yr_object_print_data( YR_OBJECT* object, int indent, int print_identifier); #endif yara-4.1.3/libyara/include/yara/parser.h000066400000000000000000000075601413423160300201270ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_PARSER_H #define YR_PARSER_H #include "lexer.h" int yr_parser_emit( yyscan_t yyscanner, uint8_t instruction, YR_ARENA_REF* instruction_ref); int yr_parser_emit_with_arg( yyscan_t yyscanner, uint8_t instruction, int64_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref); int yr_parser_emit_with_arg_int32( yyscan_t yyscanner, uint8_t instruction, int32_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref); int yr_parser_emit_with_arg_double( yyscan_t yyscanner, uint8_t instruction, double argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref); int yr_parser_emit_with_arg_reloc( yyscan_t yyscanner, uint8_t instruction, void* argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref); int yr_parser_emit_push_const(yyscan_t yyscanner, uint64_t argument); int yr_parser_check_types( YR_COMPILER* compiler, YR_OBJECT_FUNCTION* function, const char* actual_args_fmt); int yr_parser_lookup_string( yyscan_t yyscanner, const char* identifier, YR_STRING** string); int yr_parser_lookup_loop_variable( yyscan_t yyscanner, const char* identifier, YR_EXPRESSION*); int yr_parser_reduce_rule_declaration_phase_1( yyscan_t yyscanner, int32_t flags, const char* identifier, YR_ARENA_REF* rule_ref); int yr_parser_reduce_rule_declaration_phase_2( yyscan_t yyscanner, YR_ARENA_REF* rule_ref); int yr_parser_reduce_string_declaration( yyscan_t yyscanner, YR_MODIFIER modifier, const char* identifier, SIZED_STRING* str, YR_ARENA_REF* string_ref); int yr_parser_reduce_meta_declaration( yyscan_t yyscanner, int32_t type, const char* identifier, const char* string, int64_t integer, YR_ARENA_REF* meta_ref); int yr_parser_reduce_string_identifier( yyscan_t yyscanner, const char* identifier, uint8_t instruction, uint64_t at_offset); int yr_parser_emit_pushes_for_strings( yyscan_t yyscanner, const char* identifier); int yr_parser_reduce_external( yyscan_t yyscanner, const char* identifier, uint8_t instruction); int yr_parser_reduce_import(yyscan_t yyscanner, SIZED_STRING* module_name); int yr_parser_reduce_operation( yyscan_t yyscanner, const char* operation, YR_EXPRESSION left_operand, YR_EXPRESSION right_operand); #endif yara-4.1.3/libyara/include/yara/pe.h000066400000000000000000000412271413423160300172350ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_PE_H #define YR_PE_H #include #include #pragma pack(push, 1) #if defined(_WIN32) || defined(__CYGWIN__) #include // PKCS7_SIGNER_INFO is defined by wincrypt.h, but it conflicts with a type // defined in openssl/pkcs7.h which is used in pe.c. Let's undefine the macro. #undef PKCS7_SIGNER_INFO // These definitions are not present in older Windows headers. #ifndef IMAGE_FILE_MACHINE_ARMNT #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 #endif #ifndef IMAGE_FILE_MACHINE_ARM64 #define IMAGE_FILE_MACHINE_ARM64 0xaa64 #endif #else #include #include typedef uint8_t BYTE; typedef uint16_t WORD; typedef uint32_t DWORD; typedef int32_t LONG; typedef uint32_t ULONG; typedef uint64_t ULONGLONG; #ifndef _MAC #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_OS2_SIGNATURE 0x454E // NE #define IMAGE_OS2_SIGNATURE_LE 0x454C // LE #define IMAGE_VXD_SIGNATURE 0x454C // LE #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 #else #define IMAGE_DOS_SIGNATURE 0x4D5A // MZ #define IMAGE_OS2_SIGNATURE 0x4E45 // NE #define IMAGE_OS2_SIGNATURE_LE 0x4C45 // LE #define IMAGE_NT_SIGNATURE 0x50450000 // PE00 #endif #pragma pack(push, 2) typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; #pragma pack(pop) // // File header format. // #pragma pack(push, 4) typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; #define IMAGE_SIZEOF_FILE_HEADER 20 // Relocation info stripped from file. #define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // File is executable (i.e. no unresolved external references). #define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // Line numbers stripped from file. #define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Local symbols stripped from file. #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Aggressively trim working set #define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // App can handle >2gb addresses #define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // Bytes of machine word are reversed. #define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // 32 bit word machine. #define IMAGE_FILE_32BIT_MACHINE 0x0100 // Debugging info stripped from file in .DBG file #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // If Image is on removable media, copy and run from the swap file. #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on Net, copy and run from the swap file. #define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // System File. #define IMAGE_FILE_SYSTEM 0x1000 // File is a DLL.s #define IMAGE_FILE_DLL 0x2000 // File should only be run on a UP machine #define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // Bytes of machine word are reversed. #define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 #define IMAGE_FILE_MACHINE_UNKNOWN 0x0000 #define IMAGE_FILE_MACHINE_AM33 0x01d3 #define IMAGE_FILE_MACHINE_AMD64 0x8664 #define IMAGE_FILE_MACHINE_ARM 0x01c0 #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 #define IMAGE_FILE_MACHINE_ARM64 0xaa64 #define IMAGE_FILE_MACHINE_EBC 0x0ebc #define IMAGE_FILE_MACHINE_I386 0x014c #define IMAGE_FILE_MACHINE_IA64 0x0200 #define IMAGE_FILE_MACHINE_M32R 0x9041 #define IMAGE_FILE_MACHINE_MIPS16 0x0266 #define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 #define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 #define IMAGE_FILE_MACHINE_POWERPC 0x01f0 #define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 #define IMAGE_FILE_MACHINE_R4000 0x0166 #define IMAGE_FILE_MACHINE_SH3 0x01a2 #define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 #define IMAGE_FILE_MACHINE_SH4 0x01a6 #define IMAGE_FILE_MACHINE_SH5 0x01a8 #define IMAGE_FILE_MACHINE_THUMB 0x01c2 #define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // Section characteristics #define IMAGE_SCN_CNT_CODE 0x00000020 #define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 #define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 #define IMAGE_SCN_GPREL 0x00008000 #define IMAGE_SCN_MEM_16BIT 0x00020000 #define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 #define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 #define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 #define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 #define IMAGE_SCN_MEM_SHARED 0x10000000 #define IMAGE_SCN_MEM_EXECUTE 0x20000000 #define IMAGE_SCN_MEM_READ 0x40000000 #define IMAGE_SCN_MEM_WRITE 0x80000000 // // Directory format. // typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 #define IMAGE_DIRECTORY_ENTRY_TLS 9 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 #define IMAGE_DIRECTORY_ENTRY_IAT 12 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // // Optional header format. // typedef struct _IMAGE_OPTIONAL_HEADER32 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; typedef struct _IMAGE_OPTIONAL_HEADER64 { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; ULONGLONG ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; ULONGLONG SizeOfStackReserve; ULONGLONG SizeOfStackCommit; ULONGLONG SizeOfHeapReserve; ULONGLONG SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b typedef struct _IMAGE_NT_HEADERS32 { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; typedef struct _IMAGE_NT_HEADERS64 { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER64 OptionalHeader; } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; // IMAGE_FIRST_SECTION doesn't need 32/64 versions since the file header is // the same either way. #define IMAGE_FIRST_SECTION(ntheader) \ ((PIMAGE_SECTION_HEADER)( \ (BYTE*) ntheader + offsetof(IMAGE_NT_HEADERS32, OptionalHeader) + \ yr_le16toh(((PIMAGE_NT_HEADERS32)(ntheader)) \ ->FileHeader.SizeOfOptionalHeader))) // Subsystem Values #define IMAGE_SUBSYSTEM_UNKNOWN 0 #define IMAGE_SUBSYSTEM_NATIVE 1 #define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 #define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 #define IMAGE_SUBSYSTEM_OS2_CUI 5 #define IMAGE_SUBSYSTEM_POSIX_CUI 7 #define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 #define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 #define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 #define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 #define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 #define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13 #define IMAGE_SUBSYSTEM_XBOX 14 #define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 // DllCharacteristics values #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 #define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 #define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 #define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 #define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 #define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 #define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 #define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 // // Section header format. // #define IMAGE_SIZEOF_SHORT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; #define IMAGE_SIZEOF_SECTION_HEADER 40 typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; DWORD AddressOfNames; DWORD AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR; typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[1]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; DWORD Function; DWORD Ordinal; DWORD AddressOfData; } u1; } IMAGE_THUNK_DATA32, *PIMAGE_THUNK_DATA32; #define IMAGE_ORDINAL_FLAG32 0x80000000 #define IMAGE_ORDINAL_FLAG64 0x8000000000000000L typedef struct _IMAGE_THUNK_DATA64 { union { ULONGLONG ForwarderString; ULONGLONG Function; ULONGLONG Ordinal; ULONGLONG AddressOfData; } u1; } IMAGE_THUNK_DATA64, *PIMAGE_THUNK_DATA64; typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { DWORD Name; DWORD OffsetToData; } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY; typedef struct _IMAGE_RESOURCE_DATA_ENTRY { DWORD OffsetToData; DWORD Size; DWORD CodePage; DWORD Reserved; } IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY; typedef struct _IMAGE_RESOURCE_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; WORD NumberOfNamedEntries; WORD NumberOfIdEntries; } IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY; #define IMAGE_DEBUG_TYPE_UNKNOWN 0 #define IMAGE_DEBUG_TYPE_COFF 1 #define IMAGE_DEBUG_TYPE_CODEVIEW 2 #define IMAGE_DEBUG_TYPE_FPO 3 #define IMAGE_DEBUG_TYPE_MISC 4 #define IMAGE_DEBUG_TYPE_EXCEPTION 5 #define IMAGE_DEBUG_TYPE_FIXUP 6 #define IMAGE_DEBUG_TYPE_BORLAND 9 typedef struct _IMAGE_DEBUG_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Type; DWORD SizeOfData; DWORD AddressOfRawData; DWORD PointerToRawData; } IMAGE_DEBUG_DIRECTORY, *PIMAGE_DEBUG_DIRECTORY; #pragma pack(pop) #endif // _WIN32 #define CVINFO_PDB70_CVSIGNATURE 0x53445352 // "RSDS" #define CVINFO_PDB20_CVSIGNATURE 0x3031424e // "NB10" typedef struct _CV_HEADER { DWORD dwSignature; DWORD dwOffset; } CV_HEADER, *PCV_HEADER; typedef struct _CV_INFO_PDB20 { CV_HEADER CvHeader; DWORD dwSignature; DWORD dwAge; BYTE PdbFileName[1]; } CV_INFO_PDB20, *PCV_INFO_PDB20; typedef struct _CV_INFO_PDB70 { DWORD CvSignature; DWORD Signature[4]; DWORD Age; BYTE PdbFileName[1]; } CV_INFO_PDB70, *PCV_INFO_PDB70; typedef struct _VERSION_INFO { WORD Length; WORD ValueLength; WORD Type; char Key[0]; } VERSION_INFO, *PVERSION_INFO; #define MAX_PE_CERTS 16 #define WIN_CERT_REVISION_1_0 0x0100 #define WIN_CERT_REVISION_2_0 0x0200 #define WIN_CERT_TYPE_X509 0x0001 #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 #define WIN_CERT_TYPE_RESERVED_1 0x0003 #define WIN_CERT_TYPE_TS_STACK_SIGNED 0x0004 #define WIN_CERTIFICATE_HEADER_SIZE 8 typedef struct _WIN_CERTIFICATE { DWORD Length; WORD Revision; WORD CertificateType; BYTE Certificate[0]; } WIN_CERTIFICATE, *PWIN_CERTIFICATE; #define SPC_NESTED_SIGNATURE_OBJID "1.3.6.1.4.1.311.2.4.1" // // Rich signature. // http://www.ntcore.com/files/richsign.htm // #define RICH_VERSION_ID(id_version) (id_version >> 16) #define RICH_VERSION_VERSION(id_version) (id_version & 0xFFFF) typedef struct _RICH_VERSION_INFO { DWORD id_version; // tool id and version (use RICH_VERSION_ID and // RICH_VERSION_VERSION macros) DWORD times; // number of times this tool was used } RICH_VERSION_INFO, *PRICH_VERSION_INFO; typedef struct _RICH_SIGNATURE { DWORD dans; DWORD key1; DWORD key2; DWORD key3; RICH_VERSION_INFO versions[0]; } RICH_SIGNATURE, *PRICH_SIGNATURE; #define RICH_DANS 0x536e6144 // "DanS" #define RICH_RICH 0x68636952 // "Rich" #pragma pack(pop) #endif yara-4.1.3/libyara/include/yara/pe_utils.h000066400000000000000000000042411413423160300204500ustar00rootroot00000000000000#ifndef YR_PE_UTILS_H #define YR_PE_UTILS_H #include #define MAX_PE_SECTIONS 96 #define IS_64BITS_PE(pe) \ (yr_le16toh(pe->header64->OptionalHeader.Magic) == \ IMAGE_NT_OPTIONAL_HDR64_MAGIC) #define OptionalHeader(pe, field) \ (IS_64BITS_PE(pe) ? pe->header64->OptionalHeader.field \ : pe->header->OptionalHeader.field) // // Imports are stored in a linked list. Each node (IMPORTED_DLL) contains the // name of the DLL and a pointer to another linked list of // IMPORT_EXPORT_FUNCTION structures containing the details of imported // functions. // typedef struct _IMPORTED_DLL { char* name; struct _IMPORT_FUNCTION* functions; struct _IMPORTED_DLL* next; } IMPORTED_DLL, *PIMPORTED_DLL; // // This is used to track imported and exported functions. The "has_ordinal" // field is only used in the case of imports as those are optional. Every export // has an ordinal so we don't need the field there, but in the interest of // keeping duplicate code to a minimum we use this function for both imports and // exports. // typedef struct _IMPORT_FUNCTION { char* name; uint8_t has_ordinal; uint16_t ordinal; struct _IMPORT_FUNCTION* next; } IMPORT_FUNCTION, *PIMPORT_FUNCTION; typedef struct _PE { const uint8_t* data; size_t data_size; union { PIMAGE_NT_HEADERS32 header; PIMAGE_NT_HEADERS64 header64; }; YR_HASH_TABLE* hash_table; YR_OBJECT* object; IMPORTED_DLL* imported_dlls; uint32_t resources; } PE; #define fits_in_pe(pe, pointer, size) \ ((size_t)(size) <= pe->data_size && (uint8_t*) (pointer) >= pe->data && \ (uint8_t*) (pointer) <= pe->data + pe->data_size - (size)) #define struct_fits_in_pe(pe, pointer, struct_type) \ fits_in_pe(pe, pointer, sizeof(struct_type)) PIMAGE_NT_HEADERS32 pe_get_header(const uint8_t* data, size_t data_size); PIMAGE_DATA_DIRECTORY pe_get_directory_entry(PE* pe, int entry); int64_t pe_rva_to_offset(PE* pe, uint64_t rva); char* ord_lookup(char* dll, uint16_t ord); #if HAVE_LIBCRYPTO #include time_t ASN1_get_time_t(const ASN1_TIME* time); #endif #endif yara-4.1.3/libyara/include/yara/proc.h000066400000000000000000000042071413423160300175710ustar00rootroot00000000000000/* Copyright (c) 2007. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_PROC_H #define YR_PROC_H #include typedef struct _YR_PROC_ITERATOR_CTX { const uint8_t* buffer; size_t buffer_size; YR_MEMORY_BLOCK current_block; void* proc_info; } YR_PROC_ITERATOR_CTX; YR_API int yr_process_open_iterator( int pid, YR_MEMORY_BLOCK_ITERATOR* iterator); YR_API int yr_process_close_iterator(YR_MEMORY_BLOCK_ITERATOR* iterator); YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator); YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator); YR_API const uint8_t* yr_process_fetch_memory_block_data( YR_MEMORY_BLOCK* block); #endif yara-4.1.3/libyara/include/yara/re.h000066400000000000000000000124771413423160300172440ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_RE_H #define YR_RE_H #include #include #include #include #include #define RE_MAX_RANGE INT16_MAX #define RE_NODE_LITERAL 1 #define RE_NODE_MASKED_LITERAL 2 #define RE_NODE_ANY 3 #define RE_NODE_CONCAT 4 #define RE_NODE_ALT 5 #define RE_NODE_RANGE 6 #define RE_NODE_STAR 7 #define RE_NODE_PLUS 8 #define RE_NODE_CLASS 9 #define RE_NODE_WORD_CHAR 10 #define RE_NODE_NON_WORD_CHAR 11 #define RE_NODE_SPACE 12 #define RE_NODE_NON_SPACE 13 #define RE_NODE_DIGIT 14 #define RE_NODE_NON_DIGIT 15 #define RE_NODE_EMPTY 16 #define RE_NODE_ANCHOR_START 17 #define RE_NODE_ANCHOR_END 18 #define RE_NODE_WORD_BOUNDARY 19 #define RE_NODE_NON_WORD_BOUNDARY 20 #define RE_NODE_RANGE_ANY 21 #define RE_OPCODE_ANY 0xA0 #define RE_OPCODE_LITERAL 0xA2 #define RE_OPCODE_MASKED_LITERAL 0xA4 #define RE_OPCODE_CLASS 0xA5 #define RE_OPCODE_WORD_CHAR 0xA7 #define RE_OPCODE_NON_WORD_CHAR 0xA8 #define RE_OPCODE_SPACE 0xA9 #define RE_OPCODE_NON_SPACE 0xAA #define RE_OPCODE_DIGIT 0xAB #define RE_OPCODE_NON_DIGIT 0xAC #define RE_OPCODE_MATCH 0xAD #define RE_OPCODE_MATCH_AT_END 0xB0 #define RE_OPCODE_MATCH_AT_START 0xB1 #define RE_OPCODE_WORD_BOUNDARY 0xB2 #define RE_OPCODE_NON_WORD_BOUNDARY 0xB3 #define RE_OPCODE_REPEAT_ANY_GREEDY 0xB4 #define RE_OPCODE_REPEAT_ANY_UNGREEDY 0xB5 #define RE_OPCODE_SPLIT_A 0xC0 #define RE_OPCODE_SPLIT_B 0xC1 #define RE_OPCODE_JUMP 0xC2 #define RE_OPCODE_REPEAT_START_GREEDY 0xC3 #define RE_OPCODE_REPEAT_END_GREEDY 0xC4 #define RE_OPCODE_REPEAT_START_UNGREEDY 0xC5 #define RE_OPCODE_REPEAT_END_UNGREEDY 0xC6 #define RE_FLAGS_FAST_REGEXP 0x02 #define RE_FLAGS_BACKWARDS 0x04 #define RE_FLAGS_EXHAUSTIVE 0x08 #define RE_FLAGS_WIDE 0x10 #define RE_FLAGS_NO_CASE 0x20 #define RE_FLAGS_SCAN 0x40 #define RE_FLAGS_DOT_ALL 0x80 #define RE_FLAGS_GREEDY 0x400 #define RE_FLAGS_UNGREEDY 0x800 typedef int RE_MATCH_CALLBACK_FUNC( const uint8_t* match, int match_length, int flags, void* args); int yr_re_ast_create(RE_AST** re_ast); void yr_re_ast_destroy(RE_AST* re_ast); void yr_re_ast_print(RE_AST* re_ast); SIZED_STRING* yr_re_ast_extract_literal(RE_AST* re_ast); int yr_re_ast_has_unbounded_quantifier_for_dot(RE_AST* re_ast); int yr_re_ast_split_at_chaining_point( RE_AST* re_ast, RE_AST** remainder_re_ast, int32_t* min_gap, int32_t* max_gap); int yr_re_ast_emit_code(RE_AST* re_ast, YR_ARENA* arena, int backwards_code); RE_NODE* yr_re_node_create(int type); void yr_re_node_destroy(RE_NODE* node); void yr_re_node_append_child(RE_NODE* node, RE_NODE* child); void yr_re_node_prepend_child(RE_NODE* node, RE_NODE* child); int yr_re_exec( YR_SCAN_CONTEXT* context, const uint8_t* code, const uint8_t* input_data, size_t input_forwards_size, size_t input_backwards_size, int flags, RE_MATCH_CALLBACK_FUNC callback, void* callback_args, int* matches); int yr_re_fast_exec( YR_SCAN_CONTEXT* context, const uint8_t* code, const uint8_t* input_data, size_t input_forwards_size, size_t input_backwards_size, int flags, RE_MATCH_CALLBACK_FUNC callback, void* callback_args, int* matches); int yr_re_parse(const char* re_string, RE_AST** re_ast, RE_ERROR* error); int yr_re_parse_hex(const char* hex_string, RE_AST** re_ast, RE_ERROR* error); int yr_re_compile( const char* re_string, int flags, YR_ARENA* arena, YR_ARENA_REF* ref, RE_ERROR* error); int yr_re_match(YR_SCAN_CONTEXT* context, RE* re, const char* target); #endif yara-4.1.3/libyara/include/yara/re_lexer.h000066400000000000000000000063751413423160300204430ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #undef yyparse #undef yylex #undef yyerror #undef yyfatal #undef yychar #undef yydebug #undef yynerrs #undef yyget_extra #undef yyget_lineno #undef YY_FATAL_ERROR #undef YY_DECL #undef LEX_ENV #define yyparse re_yyparse #define yylex re_yylex #define yyerror re_yyerror #define yyfatal re_yyfatal #define yychar re_yychar #define yydebug re_yydebug #define yynerrs re_yynerrs #define yyget_extra re_yyget_extra #define yyget_lineno re_yyget_lineno #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif #define YY_EXTRA_TYPE RE_AST* #define YY_USE_CONST typedef struct _RE_LEX_ENVIRONMENT { RE_CLASS re_class; int last_error; char last_error_message[256]; } RE_LEX_ENVIRONMENT; #define LEX_ENV ((RE_LEX_ENVIRONMENT*) lex_env) // The default behavior when a fatal error occurs in the parser is calling // exit(YY_EXIT_FAILURE) for terminating the process. This is not acceptable // for a library, which should return gracefully to the calling program. For // this reason we redefine the YY_FATAL_ERROR macro so that it expands to our // own function instead of the one provided by default. #define YY_FATAL_ERROR(msg) re_yyfatal(yyscanner, msg) #include #define YY_DECL \ int re_yylex( \ YYSTYPE* yylval_param, yyscan_t yyscanner, RE_LEX_ENVIRONMENT* lex_env) YY_EXTRA_TYPE yyget_extra(yyscan_t yyscanner); int yylex( YYSTYPE* yylval_param, yyscan_t yyscanner, RE_LEX_ENVIRONMENT* lex_env); void yyerror( yyscan_t yyscanner, RE_LEX_ENVIRONMENT* lex_env, const char* error_message); void yyfatal(yyscan_t yyscanner, const char* error_message); int yyparse(void* yyscanner, RE_LEX_ENVIRONMENT* lex_env); int yr_parse_re_string(const char* re_string, RE_AST** re_ast, RE_ERROR* error); yara-4.1.3/libyara/include/yara/rules.h000066400000000000000000000121431413423160300177560ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_RULES_H #define YR_RULES_H #include #include #include #include #define CALLBACK_MSG_RULE_MATCHING 1 #define CALLBACK_MSG_RULE_NOT_MATCHING 2 #define CALLBACK_MSG_SCAN_FINISHED 3 #define CALLBACK_MSG_IMPORT_MODULE 4 #define CALLBACK_MSG_MODULE_IMPORTED 5 #define CALLBACK_MSG_TOO_MANY_MATCHES 6 #define CALLBACK_MSG_RULE_MATCHING 1 #define CALLBACK_MSG_RULE_NOT_MATCHING 2 #define CALLBACK_MSG_SCAN_FINISHED 3 #define CALLBACK_MSG_IMPORT_MODULE 4 #define CALLBACK_MSG_MODULE_IMPORTED 5 #define CALLBACK_CONTINUE 0 #define CALLBACK_ABORT 1 #define CALLBACK_ERROR 2 #define yr_rule_tags_foreach(rule, tag_name) \ for (tag_name = rule->tags; tag_name != NULL && *tag_name != '\0'; \ tag_name += strlen(tag_name) + 1) #define yr_rule_metas_foreach(rule, meta) \ for (meta = rule->metas; meta != NULL; \ meta = META_IS_LAST_IN_RULE(meta) ? NULL : meta + 1) #define yr_rule_strings_foreach(rule, string) \ for (string = rule->strings; string != NULL; \ string = STRING_IS_LAST_IN_RULE(string) ? NULL : string + 1) #define yr_string_matches_foreach(context, string, match) \ for (match = context->matches[string->idx].head; match != NULL; \ match = match->next) \ /* private matches are skipped */ \ if (match->is_private) \ { \ continue; \ } \ else /* user code block goes here */ #define yr_rules_foreach(rules, rule) \ for (rule = rules->rules_table; !RULE_IS_NULL(rule); rule++) YR_API int yr_rules_scan_mem_blocks( YR_RULES* rules, YR_MEMORY_BLOCK_ITERATOR* iterator, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout); YR_API int yr_rules_scan_mem( YR_RULES* rules, const uint8_t* buffer, size_t buffer_size, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout); YR_API int yr_rules_scan_file( YR_RULES* rules, const char* filename, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout); YR_API int yr_rules_scan_fd( YR_RULES* rules, YR_FILE_DESCRIPTOR fd, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout); YR_API int yr_rules_scan_proc( YR_RULES* rules, int pid, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout); YR_API int yr_rules_save(YR_RULES* rules, const char* filename); YR_API int yr_rules_save_stream(YR_RULES* rules, YR_STREAM* stream); YR_API int yr_rules_load(const char* filename, YR_RULES** rules); YR_API int yr_rules_load_stream(YR_STREAM* stream, YR_RULES** rules); YR_API int yr_rules_destroy(YR_RULES* rules); YR_API int yr_rules_define_integer_variable( YR_RULES* rules, const char* identifier, int64_t value); YR_API int yr_rules_define_boolean_variable( YR_RULES* rules, const char* identifier, int value); YR_API int yr_rules_define_float_variable( YR_RULES* rules, const char* identifier, double value); YR_API int yr_rules_define_string_variable( YR_RULES* rules, const char* identifier, const char* value); YR_API int yr_rules_get_stats(YR_RULES* rules, YR_RULES_STATS* stats); YR_API void yr_rule_disable(YR_RULE* rule); YR_API void yr_rule_enable(YR_RULE* rule); int yr_rules_from_arena(YR_ARENA* arena, YR_RULES** rules); #endif yara-4.1.3/libyara/include/yara/scan.h000066400000000000000000000040071413423160300175500ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_SCAN_H #define YR_SCAN_H #include // // Flags used with yr_scanner_set_flags and yr_rules_scan_xxx functions. // #define SCAN_FLAGS_FAST_MODE 1 #define SCAN_FLAGS_PROCESS_MEMORY 2 #define SCAN_FLAGS_NO_TRYCATCH 4 #define SCAN_FLAGS_REPORT_RULES_MATCHING 8 #define SCAN_FLAGS_REPORT_RULES_NOT_MATCHING 16 int yr_scan_verify_match( YR_SCAN_CONTEXT* context, YR_AC_MATCH* ac_match, const uint8_t* data, size_t data_size, uint64_t data_base, size_t offset); #endif yara-4.1.3/libyara/include/yara/scanner.h000066400000000000000000000063521413423160300202620ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_SCANNER_H #define YR_SCANNER_H #include #include #include typedef YR_SCAN_CONTEXT YR_SCANNER; YR_API int yr_scanner_create(YR_RULES* rules, YR_SCANNER** scanner); YR_API void yr_scanner_destroy(YR_SCANNER* scanner); YR_API void yr_scanner_set_callback( YR_SCANNER* scanner, YR_CALLBACK_FUNC callback, void* user_data); YR_API void yr_scanner_set_timeout(YR_SCANNER* scanner, int timeout); YR_API void yr_scanner_set_flags(YR_SCANNER* scanner, int flags); YR_API int yr_scanner_define_integer_variable( YR_SCANNER* scanner, const char* identifier, int64_t value); YR_API int yr_scanner_define_boolean_variable( YR_SCANNER* scanner, const char* identifier, int value); YR_API int yr_scanner_define_float_variable( YR_SCANNER* scanner, const char* identifier, double value); YR_API int yr_scanner_define_string_variable( YR_SCANNER* scanner, const char* identifier, const char* value); YR_API int yr_scanner_scan_mem_blocks( YR_SCANNER* scanner, YR_MEMORY_BLOCK_ITERATOR* iterator); YR_API int yr_scanner_scan_mem( YR_SCANNER* scanner, const uint8_t* buffer, size_t buffer_size); YR_API int yr_scanner_scan_file(YR_SCANNER* scanner, const char* filename); YR_API int yr_scanner_scan_fd(YR_SCANNER* scanner, YR_FILE_DESCRIPTOR fd); YR_API int yr_scanner_scan_proc(YR_SCANNER* scanner, int pid); YR_API YR_RULE* yr_scanner_last_error_rule(YR_SCANNER* scanner); YR_API YR_STRING* yr_scanner_last_error_string(YR_SCANNER* scanner); YR_API YR_RULE_PROFILING_INFO* yr_scanner_get_profiling_info( YR_SCANNER* scanner); YR_API void yr_scanner_reset_profiling_info(YR_SCANNER* scanner); YR_API int yr_scanner_print_profiling_info(YR_SCANNER* scanner); #endif yara-4.1.3/libyara/include/yara/sizedstr.h000066400000000000000000000055031413423160300204750ustar00rootroot00000000000000/* Copyright (c) 2007-2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _SIZEDSTR_H #define _SIZEDSTR_H #include #include // SIZED_STRING_FLAGS_NO_CASE indicates that the has been decorated with // the "nocase" modifier or with the /i modifier in the case of regular // expressions. #define SIZED_STRING_FLAGS_NO_CASE 1 // SIZED_STRING_FLAGS_DOT_ALL is used for strings that contain a regular // expression that had the /s modifier. #define SIZED_STRING_FLAGS_DOT_ALL 2 #pragma pack(push) #pragma pack(1) // // This struct is used to support strings containing null chars. The length of // the string is stored along the string data. However the string data is also // terminated with a null char. // typedef struct _SIZED_STRING { uint32_t length; uint32_t flags; char c_string[1]; } SIZED_STRING; #pragma pack(pop) int ss_compare(SIZED_STRING* s1, SIZED_STRING* s2); int ss_icompare(SIZED_STRING* s1, SIZED_STRING* s2); bool ss_contains(SIZED_STRING* s1, SIZED_STRING* s2); bool ss_icontains(SIZED_STRING* s1, SIZED_STRING* s2); bool ss_startswith(SIZED_STRING* s1, SIZED_STRING* s2); bool ss_istartswith(SIZED_STRING* s1, SIZED_STRING* s2); bool ss_endswith(SIZED_STRING* s1, SIZED_STRING* s2); bool ss_iendswith(SIZED_STRING* s1, SIZED_STRING* s2); SIZED_STRING* ss_dup(SIZED_STRING* s); SIZED_STRING* ss_new(const char* s); SIZED_STRING* ss_convert_to_wide(SIZED_STRING* s); #endif yara-4.1.3/libyara/include/yara/stack.h000066400000000000000000000043111413423160300177270ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_STACK_H #define YR_STACK_H typedef struct YR_STACK YR_STACK; struct YR_STACK { // Pointer to a heap-allocated array containing the void* values put in // in the stack. This array starts with a fixed size and it's grown as // required when new items are pushed into the stack. void* items; // Current capacity (i.e: the number of items that fit into the array) int capacity; // Size of each individual item in the stack. int item_size; // Index of the stack's top in the items array. int top; }; int yr_stack_create(int initial_capacity, int item_size, YR_STACK** stack); void yr_stack_destroy(YR_STACK* stack); int yr_stack_push(YR_STACK* stack, void* item); int yr_stack_pop(YR_STACK* stack, void* item); #endif yara-4.1.3/libyara/include/yara/stopwatch.h000066400000000000000000000044601413423160300206430ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_STOPWATCH_H #define YR_STOPWATCH_H #include #include #if defined(_WIN32) #include typedef struct _YR_STOPWATCH { LARGE_INTEGER frequency; LARGE_INTEGER start; } YR_STOPWATCH; #elif defined(__APPLE__) && defined(__MACH__) #include typedef struct _YR_STOPWATCH { mach_timebase_info_data_t timebase; uint64_t start; } YR_STOPWATCH; #else #include typedef struct _YR_STOPWATCH { union { struct timeval tv_start; struct timespec ts_start; }; } YR_STOPWATCH; #endif // yr_stopwatch_start starts measuring time. void yr_stopwatch_start(YR_STOPWATCH* stopwatch); // yr_stopwatch_elapsed_ns returns the number of nanoseconds elapsed // since the last call to yr_stopwatch_start. uint64_t yr_stopwatch_elapsed_ns(YR_STOPWATCH* stopwatch); #endif yara-4.1.3/libyara/include/yara/stream.h000066400000000000000000000040661413423160300201240ustar00rootroot00000000000000/* Copyright (c) 2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_STREAM_H #define YR_STREAM_H #include typedef size_t (*YR_STREAM_READ_FUNC)( void* ptr, size_t size, size_t count, void* user_data); typedef size_t (*YR_STREAM_WRITE_FUNC)( const void* ptr, size_t size, size_t count, void* user_data); typedef struct _YR_STREAM { void* user_data; YR_STREAM_READ_FUNC read; YR_STREAM_WRITE_FUNC write; } YR_STREAM; size_t yr_stream_read(void* ptr, size_t size, size_t count, YR_STREAM* stream); size_t yr_stream_write( const void* ptr, size_t size, size_t count, YR_STREAM* stream); #endif yara-4.1.3/libyara/include/yara/strutils.h000066400000000000000000000053511413423160300205200ustar00rootroot00000000000000/* Copyright (c) 2007-2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_STRUTILS_H #define YR_STRUTILS_H #include #include #include #include #if defined(_WIN32) #if !defined(PRIu64) #define PRIu64 "I64u" #endif #if !defined(PRIu32) #define PRIu32 "I32u" #endif #if !defined(PRIx64) #define PRIx64 "I64x" #endif #if !defined(PRId64) #define PRId64 "I64d" #endif #if !defined(PRIi32) #define PRIi32 "I32i" #endif #else #include #endif // Cygwin already has these functions. #if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_MSC_VER) && _MSC_VER < 1900 #if !defined(snprintf) #define snprintf _snprintf #endif #endif #define strcasecmp _stricmp #define strncasecmp _strnicmp #endif uint64_t xtoi(const char* hexstr); #if !HAVE_STRLCPY && !defined(strlcpy) size_t strlcpy(char* dst, const char* src, size_t size); #endif #if !HAVE_STRLCAT && !defined(strlcat) size_t strlcat(char* dst, const char* src, size_t size); #endif #if !HAVE_MEMMEM && !defined(memmem) void* memmem( const void* haystack, size_t haystack_size, const void* needle, size_t needle_size); #endif int strnlen_w(const char* w_str); int strcmp_w(const char* w_str, const char* str); size_t strlcpy_w(char* dst, const char* w_src, size_t n); #endif int yr_isalnum(const uint8_t* s); yara-4.1.3/libyara/include/yara/threading.h000066400000000000000000000044061413423160300205740ustar00rootroot00000000000000/* Copyright (c) 2016. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_MUTEX_H #define YR_MUTEX_H #if defined(_WIN32) || defined(__CYGWIN__) #include typedef DWORD YR_THREAD_ID; typedef DWORD YR_THREAD_STORAGE_KEY; typedef HANDLE YR_MUTEX; #define YR_TLS __declspec(thread) #else #include typedef pthread_t YR_THREAD_ID; typedef pthread_key_t YR_THREAD_STORAGE_KEY; typedef pthread_mutex_t YR_MUTEX; #define YR_TLS __thread #endif YR_THREAD_ID yr_current_thread_id(void); int yr_mutex_create(YR_MUTEX*); int yr_mutex_destroy(YR_MUTEX*); int yr_mutex_lock(YR_MUTEX*); int yr_mutex_unlock(YR_MUTEX*); int yr_thread_storage_create(YR_THREAD_STORAGE_KEY*); int yr_thread_storage_destroy(YR_THREAD_STORAGE_KEY*); int yr_thread_storage_set_value(YR_THREAD_STORAGE_KEY*, void*); void* yr_thread_storage_get_value(YR_THREAD_STORAGE_KEY*); #endif yara-4.1.3/libyara/include/yara/types.h000066400000000000000000000701451413423160300177760ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_TYPES_H #define YR_TYPES_H #include #include #include #include #include #include #include #include #include "notebook.h" #define DECLARE_REFERENCE(type, name) \ union \ { \ type name; \ YR_ARENA_REF name##_; \ } YR_ALIGN(8) // Flags for YR_RULE #define RULE_FLAGS_PRIVATE 0x01 #define RULE_FLAGS_GLOBAL 0x02 #define RULE_FLAGS_NULL 0x04 #define RULE_FLAGS_DISABLED 0x08 #define RULE_IS_PRIVATE(x) (((x)->flags) & RULE_FLAGS_PRIVATE) #define RULE_IS_GLOBAL(x) (((x)->flags) & RULE_FLAGS_GLOBAL) #define RULE_IS_NULL(x) (((x)->flags) & RULE_FLAGS_NULL) #define RULE_IS_DISABLED(x) (((x)->flags) & RULE_FLAGS_DISABLED) // Flags for YR_STRING #define STRING_FLAGS_REFERENCED 0x01 #define STRING_FLAGS_HEXADECIMAL 0x02 #define STRING_FLAGS_NO_CASE 0x04 #define STRING_FLAGS_ASCII 0x08 #define STRING_FLAGS_WIDE 0x10 #define STRING_FLAGS_REGEXP 0x20 #define STRING_FLAGS_FAST_REGEXP 0x40 #define STRING_FLAGS_FULL_WORD 0x80 #define STRING_FLAGS_ANONYMOUS 0x100 #define STRING_FLAGS_SINGLE_MATCH 0x200 #define STRING_FLAGS_LITERAL 0x400 #define STRING_FLAGS_FITS_IN_ATOM 0x800 #define STRING_FLAGS_LAST_IN_RULE 0x1000 #define STRING_FLAGS_CHAIN_PART 0x2000 #define STRING_FLAGS_CHAIN_TAIL 0x4000 #define STRING_FLAGS_FIXED_OFFSET 0x8000 #define STRING_FLAGS_GREEDY_REGEXP 0x10000 #define STRING_FLAGS_DOT_ALL 0x20000 #define STRING_FLAGS_DISABLED 0x40000 #define STRING_FLAGS_XOR 0x80000 #define STRING_FLAGS_PRIVATE 0x100000 #define STRING_FLAGS_BASE64 0x200000 #define STRING_FLAGS_BASE64_WIDE 0x400000 #define STRING_IS_HEX(x) (((x)->flags) & STRING_FLAGS_HEXADECIMAL) #define STRING_IS_NO_CASE(x) (((x)->flags) & STRING_FLAGS_NO_CASE) #define STRING_IS_DOT_ALL(x) (((x)->flags) & STRING_FLAGS_DOT_ALL) #define STRING_IS_ASCII(x) (((x)->flags) & STRING_FLAGS_ASCII) #define STRING_IS_WIDE(x) (((x)->flags) & STRING_FLAGS_WIDE) #define STRING_IS_REGEXP(x) (((x)->flags) & STRING_FLAGS_REGEXP) #define STRING_IS_GREEDY_REGEXP(x) (((x)->flags) & STRING_FLAGS_GREEDY_REGEXP) #define STRING_IS_FULL_WORD(x) (((x)->flags) & STRING_FLAGS_FULL_WORD) #define STRING_IS_ANONYMOUS(x) (((x)->flags) & STRING_FLAGS_ANONYMOUS) #define STRING_IS_REFERENCED(x) (((x)->flags) & STRING_FLAGS_REFERENCED) #define STRING_IS_SINGLE_MATCH(x) (((x)->flags) & STRING_FLAGS_SINGLE_MATCH) #define STRING_IS_FIXED_OFFSET(x) (((x)->flags) & STRING_FLAGS_FIXED_OFFSET) #define STRING_IS_LITERAL(x) (((x)->flags) & STRING_FLAGS_LITERAL) #define STRING_IS_FAST_REGEXP(x) (((x)->flags) & STRING_FLAGS_FAST_REGEXP) #define STRING_IS_CHAIN_PART(x) (((x)->flags) & STRING_FLAGS_CHAIN_PART) #define STRING_IS_CHAIN_TAIL(x) (((x)->flags) & STRING_FLAGS_CHAIN_TAIL) #define STRING_IS_LAST_IN_RULE(x) (((x)->flags) & STRING_FLAGS_LAST_IN_RULE) #define STRING_FITS_IN_ATOM(x) (((x)->flags) & STRING_FLAGS_FITS_IN_ATOM) #define STRING_IS_DISABLED(x) (((x)->flags) & STRING_FLAGS_DISABLED) #define STRING_IS_XOR(x) (((x)->flags) & STRING_FLAGS_XOR) #define STRING_IS_BASE64(x) (((x)->flags) & STRING_FLAGS_BASE64) #define STRING_IS_BASE64_WIDE(x) (((x)->flags) & STRING_FLAGS_BASE64_WIDE) #define STRING_IS_PRIVATE(x) (((x)->flags) & STRING_FLAGS_PRIVATE) #define META_TYPE_INTEGER 1 #define META_TYPE_STRING 2 #define META_TYPE_BOOLEAN 3 #define META_FLAGS_LAST_IN_RULE 1 #define META_IS_LAST_IN_RULE(x) (((x)->flags) & META_FLAGS_LAST_IN_RULE) #define EXTERNAL_VARIABLE_TYPE_NULL 0 #define EXTERNAL_VARIABLE_TYPE_FLOAT 1 #define EXTERNAL_VARIABLE_TYPE_INTEGER 2 #define EXTERNAL_VARIABLE_TYPE_BOOLEAN 3 #define EXTERNAL_VARIABLE_TYPE_STRING 4 #define EXTERNAL_VARIABLE_TYPE_MALLOC_STRING 5 #define EXTERNAL_VARIABLE_IS_NULL(x) \ ((x) != NULL ? (x)->type == EXTERNAL_VARIABLE_TYPE_NULL : true) typedef struct RE RE; typedef struct RE_AST RE_AST; typedef struct RE_NODE RE_NODE; typedef struct RE_CLASS RE_CLASS; typedef struct RE_ERROR RE_ERROR; typedef struct RE_FIBER RE_FIBER; typedef struct RE_FIBER_LIST RE_FIBER_LIST; typedef struct RE_FIBER_POOL RE_FIBER_POOL; typedef struct YR_AC_STATE YR_AC_STATE; typedef struct YR_AC_AUTOMATON YR_AC_AUTOMATON; typedef struct YR_AC_TABLES YR_AC_TABLES; typedef struct YR_AC_MATCH_LIST_ENTRY YR_AC_MATCH_LIST_ENTRY; typedef struct YR_AC_MATCH YR_AC_MATCH; typedef struct YR_NAMESPACE YR_NAMESPACE; typedef struct YR_META YR_META; typedef struct YR_MATCHES YR_MATCHES; typedef struct YR_STRING YR_STRING; typedef struct YR_RULE YR_RULE; typedef struct YR_RULES YR_RULES; typedef struct YR_SUMMARY YR_SUMMARY; typedef struct YR_RULES_STATS YR_RULES_STATS; typedef struct YR_PROFILING_INFO YR_PROFILING_INFO; typedef struct YR_RULE_PROFILING_INFO YR_RULE_PROFILING_INFO; typedef struct YR_EXTERNAL_VARIABLE YR_EXTERNAL_VARIABLE; typedef struct YR_MATCH YR_MATCH; typedef struct YR_SCAN_CONTEXT YR_SCAN_CONTEXT; typedef union YR_VALUE YR_VALUE; typedef struct YR_VALUE_STACK YR_VALUE_STACK; typedef struct YR_OBJECT YR_OBJECT; typedef struct YR_OBJECT_STRUCTURE YR_OBJECT_STRUCTURE; typedef struct YR_OBJECT_ARRAY YR_OBJECT_ARRAY; typedef struct YR_OBJECT_DICTIONARY YR_OBJECT_DICTIONARY; typedef struct YR_OBJECT_FUNCTION YR_OBJECT_FUNCTION; typedef struct YR_STRUCTURE_MEMBER YR_STRUCTURE_MEMBER; typedef struct YR_ARRAY_ITEMS YR_ARRAY_ITEMS; typedef struct YR_DICTIONARY_ITEMS YR_DICTIONARY_ITEMS; typedef struct YR_MODULE YR_MODULE; typedef struct YR_MODULE_IMPORT YR_MODULE_IMPORT; typedef struct YR_MEMORY_BLOCK YR_MEMORY_BLOCK; typedef struct YR_MEMORY_BLOCK_ITERATOR YR_MEMORY_BLOCK_ITERATOR; typedef struct YR_MODIFIER YR_MODIFIER; typedef struct YR_ITERATOR YR_ITERATOR; typedef uint32_t YR_AC_TRANSITION; #pragma pack(push) #pragma pack(8) struct YR_NAMESPACE { // Pointer to namespace's name. DECLARE_REFERENCE(const char*, name); // Index of this namespace in the array of YR_NAMESPACE structures stored // in YR_NAMESPACES_TABLE. // // YR_ALIGN(8) forces the idx field to be treated as a 8-bytes field // and therefore the struct's size is 16 bytes. This is necessary only for // 32-bits versions of YARA compiled with Visual Studio. See: #1358. YR_ALIGN(8) uint32_t idx; }; struct YR_META { DECLARE_REFERENCE(const char*, identifier); DECLARE_REFERENCE(const char*, string); int64_t integer; int32_t type; int32_t flags; }; struct YR_STRING { // Flags, see STRING_FLAGS_XXX macros defined above. uint32_t flags; // Index of this string in the array of YR_STRING structures stored in // YR_STRINGS_TABLE. uint32_t idx; // If the string can only match at a specific offset (for example if the // condition is "$a at 0" the string $a can only match at offset 0), the // fixed_offset field contains the offset, it have the YR_UNDEFINED value for // strings that can match anywhere. int64_t fixed_offset; // Index of the rule containing this string in the array of YR_RULE // structures stored in YR_RULES_TABLE. uint32_t rule_idx; // String's length. int32_t length; // Pointer to the string itself, the length is indicated by the "length" // field. DECLARE_REFERENCE(uint8_t*, string); // Strings are splitted in two or more parts when they contain a "gap" that // is larger than YR_STRING_CHAINING_THRESHOLD. This happens in strings like // { 01 02 03 04 [X-Y] 05 06 07 08 } if Y >= X + YR_STRING_CHAINING_THRESHOLD // and also in { 01 02 03 04 [-] 05 06 07 08 }. In both cases the strings are // split in { 01 02 03 04 } and { 05 06 07 08 }, and the two smaller strings // are searched for independently. If some string S is splitted in S1 and S2, // S2 is chained to S1. In the example above { 05 06 07 08 } is chained to // { 01 02 03 04 }. The same applies when the string is splitted in more than // two parts, if S is split in S1, S2, and S3. S3 is chained to S2 and S2 is // chained to S1 (it can represented as: S1 <- S2 <- S3). DECLARE_REFERENCE(YR_STRING*, chained_to); // When this string is chained to some other string, chain_gap_min and // chain_gap_max contain the minimum and maximum distance between the two // strings. For example in { 01 02 03 04 [X-Y] 05 06 07 08 }, the string // { 05 06 07 08 } is chained to { 01 02 03 04 } and chain_gap_min is X // and chain_gap_max is Y. These fields are ignored for strings that are not // part of a string chain. int32_t chain_gap_min; int32_t chain_gap_max; // Identifier of this string. DECLARE_REFERENCE(const char*, identifier); }; struct YR_RULE { int32_t flags; // Number of atoms generated for this rule. int32_t num_atoms; DECLARE_REFERENCE(const char*, identifier); DECLARE_REFERENCE(const char*, tags); DECLARE_REFERENCE(YR_META*, metas); DECLARE_REFERENCE(YR_STRING*, strings); DECLARE_REFERENCE(YR_NAMESPACE*, ns); }; struct YR_SUMMARY { uint32_t num_rules; uint32_t num_strings; uint32_t num_namespaces; }; struct YR_EXTERNAL_VARIABLE { int32_t type; YR_ALIGN(8) union { int64_t i; double f; char* s; } value; DECLARE_REFERENCE(const char*, identifier); }; struct YR_AC_MATCH { DECLARE_REFERENCE(YR_STRING*, string); DECLARE_REFERENCE(const uint8_t*, forward_code); DECLARE_REFERENCE(const uint8_t*, backward_code); DECLARE_REFERENCE(YR_AC_MATCH*, next); // When the Aho-Corasick automaton reaches some state that has associated // matches, the current position in the input buffer is a few bytes past // the point where the match actually occurs, for example, when looking for // string "bar" in "foobarbaz", when the automaton reaches the state // associated to the ending "r" in "bar, which is the one that has a match, // the current position in the input is 6 (the "b" after the "r"), but the // match is at position 3. The backtrack field indicates how many bytes the // scanner has to go back to find the point where the match actually start. // // YR_ALIGN(8) forces the backtrack field to be treated as a 8-bytes field // and therefore the struct's size is 40 bytes. This is necessary only for // 32-bits versions of YARA compiled with Visual Studio. See: #1358. YR_ALIGN(8) uint16_t backtrack; }; #pragma pack(pop) // // Structs defined below are never stored in the compiled rules file // struct RE_NODE { int type; union { int value; int count; int start; }; union { int mask; int end; }; int greedy; RE_CLASS* re_class; RE_NODE* children_head; RE_NODE* children_tail; RE_NODE* prev_sibling; RE_NODE* next_sibling; YR_ARENA_REF forward_code_ref; YR_ARENA_REF backward_code_ref; }; struct RE_CLASS { uint8_t negated; uint8_t bitmap[32]; }; struct RE_AST { uint32_t flags; RE_NODE* root_node; }; // Disable warning due to zero length array in Microsoft's compiler #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4200) #endif struct RE { uint32_t flags; uint8_t code[0]; }; #ifdef _MSC_VER #pragma warning(pop) #endif struct RE_ERROR { char message[384]; }; struct RE_FIBER { const uint8_t* ip; // instruction pointer int32_t sp; // stack pointer int32_t rc; // repeat counter RE_FIBER* prev; RE_FIBER* next; uint16_t stack[RE_MAX_STACK]; }; struct RE_FIBER_LIST { RE_FIBER* head; RE_FIBER* tail; }; struct RE_FIBER_POOL { int fiber_count; RE_FIBER_LIST fibers; }; struct YR_MODIFIER { int32_t flags; uint8_t xor_min; uint8_t xor_max; SIZED_STRING* alphabet; }; struct YR_MATCHES { YR_MATCH* head; YR_MATCH* tail; int32_t count; }; struct YR_MATCH { int64_t base; // Base address for the match int64_t offset; // Offset relative to base for the match int32_t match_length; // Match length int32_t data_length; // Pointer to a buffer containing a portion of the matched data. The size of // the buffer is data_length. data_length is always <= length and is limited // to YR_CONFIG_MAX_MATCH_DATA bytes. const uint8_t* data; YR_MATCH* prev; YR_MATCH* next; // If the match belongs to a chained string chain_length contains the // length of the chain. This field is used only in unconfirmed matches. int32_t chain_length; // True if this is match for a private string. bool is_private; }; struct YR_AC_STATE { YR_AC_STATE* failure; YR_AC_STATE* first_child; YR_AC_STATE* siblings; // Reference to the YR_AC_MATCH structure that heads the list of matches // for this state. YR_ARENA_REF matches_ref; uint8_t depth; uint8_t input; uint32_t t_table_slot; }; struct YR_AC_MATCH_LIST_ENTRY { uint16_t backtrack; uint32_t string_idx; YR_ARENA_REF ref; YR_ARENA_REF forward_code_ref; YR_ARENA_REF backward_code_ref; YR_AC_MATCH_LIST_ENTRY* next; }; struct YR_AC_AUTOMATON { // Arena used by this automaton to store the transition and match tables. YR_ARENA* arena; // Both m_table and t_table have the same number of elements, which is // stored in tables_size. uint32_t tables_size; // The first slot in the transition table (t_table) that may be be unused. // Used for speeding up the construction of the transition table. uint32_t t_table_unused_candidate; // Bitmask where each bit indicates if the corresponding slot in the // transition table is already in use. YR_BITMASK* bitmask; // Pointer to the root Aho-Corasick state. YR_AC_STATE* root; }; struct YR_RULES { YR_ARENA* arena; // Array of pointers with an entry for each rule. The rule_idx field in the // YR_STRING structure is an index within this array. union { YR_RULE* rules_table; // The previous name for rules_table was rules_list_head, because this // was previously a linked list. The old name is maintained but marked as // deprecated, which will raise a warning if used. // TODO(vmalvarez): Remove this field when a reasonable a few versions // after 4.1 has been released. YR_RULE* rules_list_head YR_DEPRECATED; }; // Array of pointers with an entry for each of the defined strings. The idx // field in the YR_STRING structure is an index within this array. union { YR_STRING* strings_table; // The previous name for strings_table was strings_list_head, because this // was previously a linked list. The old name is maintained but marked as // deprecated, which will raise a warning if used. // TODO(vmalvarez): Remove this field when a reasonable a few versions // after 4.1 has been released. YR_STRING* strings_list_head YR_DEPRECATED; }; // Array of pointers with an entry for each external variable. union { YR_EXTERNAL_VARIABLE* ext_vars_table; // The previous name for ext_vars_table was externals_list_head, because // this was previously a linked list. The old name is maintained but marked // as deprecated, which will raise a warning if used. // TODO(vmalvarez): Remove this field when a reasonable a few versions // after 4.1 has been released. YR_EXTERNAL_VARIABLE* externals_list_head YR_DEPRECATED; }; // Pointer to the Aho-Corasick transition table. YR_AC_TRANSITION* ac_transition_table; // A pointer to the arena where YR_AC_MATCH structures are allocated. YR_AC_MATCH* ac_match_pool; // Table that translates from Aho-Corasick states (which are identified by // numbers 0, 1, 2.. and so on) to the index in ac_match_pool where the // YR_AC_MATCH structures for the corresponding state start. // If the entry corresponding to state N in ac_match_table is zero, it // means that there's no match associated to the state. If it's non-zero, // its value is the 1-based index within ac_match_pool where the first // match resides. uint32_t* ac_match_table; // Pointer to the first instruction that is executed whan evaluating the // conditions for all rules. The code is executed by yr_execute_code and // the instructions are defined by the OP_X macros in exec.h. const uint8_t* code_start; // Total number of rules. uint32_t num_rules; // Total number of strings. uint32_t num_strings; // Total number of namespaces. uint32_t num_namespaces; }; struct YR_RULES_STATS { // Total number of rules uint32_t num_rules; // Total number of strings across all rules. uint32_t num_strings; // Total number of Aho-Corasick matches. Each node in the Aho-Corasick // automaton has a list of YR_AC_MATCH_LIST_ENTRY structures (match list) // pointing to strings that are potential matches. This field holds the total // number of those structures across all nodes in the automaton. uint32_t ac_matches; // Length of the match list for the root node in the Aho-Corasick automaton. uint32_t ac_root_match_list_length; // Average number of matches per match list. float ac_average_match_list_length; // Top 10 longest match lists. uint32_t top_ac_match_list_lengths[100]; // Percentiles of match lists' lengths. If the i-th value in the array is N // then i percent of the match lists have N or less items. uint32_t ac_match_list_length_pctls[101]; // Size of Aho-Corasick transition & match tables. uint32_t ac_tables_size; }; // // YR_PROFILING_INFO contains profiling information for a rule. // struct YR_PROFILING_INFO { // Number of times that some atom belonging to the rule matched. Each // matching atom means a potential string match that needs to be verified. uint32_t atom_matches; // Amount of time (in nanoseconds) spent verifying atom matches for // determining if the corresponding string actually matched or not. This // time is not measured for all atom matches, only 1 out of 1024 matches // are actually measured. uint64_t match_time; // Amount of time (in nanoseconds) spent evaluating the rule condition. uint64_t exec_time; }; //////////////////////////////////////////////////////////////////////////////// // YR_RULE_PROFILING_INFO is the structure returned by // yr_scanner_get_profiling_info // struct YR_RULE_PROFILING_INFO { YR_RULE* rule; uint64_t cost; }; typedef const uint8_t* (*YR_MEMORY_BLOCK_FETCH_DATA_FUNC)( YR_MEMORY_BLOCK* self); typedef YR_MEMORY_BLOCK* (*YR_MEMORY_BLOCK_ITERATOR_FUNC)( YR_MEMORY_BLOCK_ITERATOR* self); typedef uint64_t (*YR_MEMORY_BLOCK_ITERATOR_SIZE_FUNC)( YR_MEMORY_BLOCK_ITERATOR* self); struct YR_MEMORY_BLOCK { size_t size; uint64_t base; void* context; YR_MEMORY_BLOCK_FETCH_DATA_FUNC fetch_data; }; /////////////////////////////////////////////////////////////////////////////// // YR_MEMORY_BLOCK_ITERATOR represents an iterator that returns a series of // memory blocks to be scanned by yr_scanner_scan_mem_blocks. The iterator have // pointers to three functions: "first", "next" and "file_size". The "first" // function is invoked for retrieving the first memory block, followed by calls // to "next" for retrieving the following blocks until "next" returns a NULL // pointer. The "file_size" function is called for obtaining the size of the // file. struct YR_MEMORY_BLOCK_ITERATOR { // A pointer that can be used by specific implementations of an iterator for // storing the iterator's state. void* context; // Pointers to functions for iterating over the memory blocks. YR_MEMORY_BLOCK_ITERATOR_FUNC first; YR_MEMORY_BLOCK_ITERATOR_FUNC next; // Pointer to a function that returns the file size as computed by the // iterator. This is a the size returned by the filesize keyword in YARA // rules. If this pointer is NULL the file size will be undefined. YR_MEMORY_BLOCK_ITERATOR_SIZE_FUNC file_size; // Error occurred during the last call to "first" or "next" functions. These // functions must set the value of last_error to ERROR_SUCCESS or to some // other error code if appropriate. Alternatively, last_error can be set to // ERROR_SUCCESS before using the iterator and changed by "first" or "next" // only when they want to report an error. int last_error; }; typedef int (*YR_CALLBACK_FUNC)( YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data); struct YR_SCAN_CONTEXT { // File size of the file being scanned. uint64_t file_size; // Entry point of the file being scanned, if the file is PE or ELF. uint64_t entry_point; // Scanning flags. int flags; // Canary value used for preventing hand-crafted objects from being embedded // in compiled rules and used to exploit YARA. The canary value is initialized // to a random value and is subsequently set to all objects created by // yr_object_create. The canary is verified when objects are used by // yr_execute_code. int canary; // Scan timeout in nanoseconds. uint64_t timeout; // Pointer to user-provided data passed to the callback function. void* user_data; // Pointer to the user-provided callback function that is called when an // event occurs during the scan (a rule matching, a module being loaded, etc) YR_CALLBACK_FUNC callback; // Pointer to the YR_RULES object associated to this scan context. YR_RULES* rules; // Pointer to the YR_STRING causing the most recent scan error. YR_STRING* last_error_string; // Pointer to the iterator used for scanning YR_MEMORY_BLOCK_ITERATOR* iterator; // Pointer to a table mapping identifiers to YR_OBJECT structures. This table // contains entries for external variables and modules. YR_HASH_TABLE* objects_table; // Notebook used for storing YR_MATCH structures associated to the matches // found. YR_NOTEBOOK* matches_notebook; // Stopwatch used for measuring the time elapsed during the scan. YR_STOPWATCH stopwatch; // Fiber pool used by yr_re_exec. RE_FIBER_POOL re_fiber_pool; // A bitmap with one bit per rule, bit N is set when the rule with index N // has matched. YR_BITMASK* rule_matches_flags; // A bitmap with one bit per namespace, bit N is set if the namespace with // index N has some global rule that is not satisfied. YR_BITMASK* ns_unsatisfied_flags; // A bitmap with one bit per string, bit N is set if the string with index // N has too many matches. YR_BITMASK* strings_temp_disabled; // Array with pointers to lists of matches. Item N in the array has the // list of matches for string with index N. YR_MATCHES* matches; // "unconfirmed_matches" is like "matches" but for strings that are part of // a chain. Let's suppose that the string S is split in two chained strings // S1 <- S2. When a match is found for S1, we can't be sure that S matches // until a match for S2 is found (within the range defined by chain_gap_min // and chain_gap_max), so the matches for S1 are put in "unconfirmed_matches" // until they can be confirmed or discarded. YR_MATCHES* unconfirmed_matches; // profiling_info is a pointer to an array of YR_PROFILING_INFO structures, // one per rule. Entry N has the profiling information for rule with index N. YR_PROFILING_INFO* profiling_info; }; union YR_VALUE { int64_t i; double d; void* p; YR_OBJECT* o; YR_STRING* s; YR_ITERATOR* it; SIZED_STRING* ss; RE* re; }; struct YR_VALUE_STACK { int32_t sp; int32_t capacity; YR_VALUE* items; }; #define OBJECT_COMMON_FIELDS \ int canary; \ int8_t type; \ const char* identifier; \ YR_OBJECT* parent; \ void* data; struct YR_OBJECT { OBJECT_COMMON_FIELDS YR_VALUE value; }; struct YR_OBJECT_STRUCTURE { OBJECT_COMMON_FIELDS YR_STRUCTURE_MEMBER* members; }; struct YR_OBJECT_ARRAY { OBJECT_COMMON_FIELDS YR_OBJECT* prototype_item; YR_ARRAY_ITEMS* items; }; struct YR_OBJECT_DICTIONARY { OBJECT_COMMON_FIELDS YR_OBJECT* prototype_item; YR_DICTIONARY_ITEMS* items; }; typedef int (*YR_MODULE_FUNC)( YR_VALUE* args, YR_SCAN_CONTEXT* context, YR_OBJECT_FUNCTION* function_obj); struct YR_OBJECT_FUNCTION { OBJECT_COMMON_FIELDS YR_OBJECT* return_obj; struct { const char* arguments_fmt; YR_MODULE_FUNC code; } prototypes[YR_MAX_OVERLOADED_FUNCTIONS]; }; #define object_as_structure(obj) ((YR_OBJECT_STRUCTURE*) (obj)) #define object_as_array(obj) ((YR_OBJECT_ARRAY*) (obj)) #define object_as_dictionary(obj) ((YR_OBJECT_DICTIONARY*) (obj)) #define object_as_function(obj) ((YR_OBJECT_FUNCTION*) (obj)) struct YR_STRUCTURE_MEMBER { YR_OBJECT* object; YR_STRUCTURE_MEMBER* next; }; struct YR_ARRAY_ITEMS { // Capacity is the size of the objects array. int capacity; // Length is determined by the last element in the array. If the index of the // last element is N, then length is N+1 because indexes start at 0. int length; YR_OBJECT* objects[1]; }; struct YR_DICTIONARY_ITEMS { int used; int free; struct { SIZED_STRING* key; YR_OBJECT* obj; } objects[1]; }; // Iterators are used in loops of the form: // // for in : ( ) // // The YR_ITERATOR struct abstracts the many different types of objects that // can be iterated. Each type of iterator must provide a "next" function which // is called multiple times for retrieving elements from the iterator. This // function is responsible for pushing the next item in the stack and a boolean // indicating if the end of the iterator has been reached. The boolean must be // pushed first, so that the next item is in the top of the stack when the // function returns. // // +------------+ // | next item | <- top of the stack // +------------+ // | false | <- false indicates that there are more items // +------------+ // | . . . | // // The boolean shouldn't be true if the next item was pushed in the stack, it // can be true only when all the items have been returned in previous calls, // in which case the value for the next item should be YR_UNDEFINED. The stack // should look like this after the last call to "next": // // +------------+ // | undefined | <- next item is undefined. // +------------+ // | true | <- true indicates that are no more items. // +------------+ // | . . . | // // We can't use the YR_UNDEFINED value in the stack as an indicator of the end // of the iterator, because it's legitimate for an iterator to return // YR_UNDEFINED items in the middle of the iteration. // // The "next" function should return ERROR_SUCCESS if everything went fine or // an error code in case of error. typedef int (*YR_ITERATOR_NEXT_FUNC)(YR_ITERATOR* self, YR_VALUE_STACK* stack); struct YR_ARRAY_ITERATOR { YR_OBJECT* array; int index; }; struct YR_DICT_ITERATOR { YR_OBJECT* dict; int index; }; struct YR_INT_RANGE_ITERATOR { int64_t next; int64_t last; }; struct YR_INT_ENUM_ITERATOR { int64_t next; int64_t count; int64_t items[1]; }; struct YR_ITERATOR { YR_ITERATOR_NEXT_FUNC next; union { struct YR_ARRAY_ITERATOR array_it; struct YR_DICT_ITERATOR dict_it; struct YR_INT_RANGE_ITERATOR int_range_it; struct YR_INT_ENUM_ITERATOR int_enum_it; }; }; #endif yara-4.1.3/libyara/include/yara/utils.h000066400000000000000000000117521413423160300177710ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef YR_UTILS_H #define YR_UTILS_H #include #include #ifndef NULL #define NULL 0 #endif #if defined(HAVE_STDBOOL_H) || (defined(_MSC_VER) && _MSC_VER >= 1800) #include #else #ifndef __cplusplus #define bool _Bool #define true 1 #define false 0 #endif /* __cplusplus */ #endif #ifdef __cplusplus #define EXTERNC extern "C" #else #define EXTERNC #endif #if defined(_WIN32) || defined(__CYGWIN__) #ifdef YR_BUILDING_DLL #ifdef __GNUC__ #define YR_API EXTERNC __attribute__((dllexport)) #define YR_DEPRECATED_API EXTERNC __attribute__((deprecated)) #define YR_DEPRECATED __attribute__((deprecated)) #else #define YR_API EXTERNC __declspec(dllexport) #define YR_DEPRECATED_API EXTERNC __declspec(deprecated) #define YR_DEPRECATED __declspec(deprecated) #endif #elif defined(YR_IMPORTING_DLL) #ifdef __GNUC__ #define YR_API EXTERNC __attribute__((dllimport)) #define YR_DEPRECATED_API EXTERNC __attribute__((deprecated)) #define YR_DEPRECATED __attribute__((deprecated)) #else #define YR_API EXTERNC __declspec(dllimport) #define YR_DEPRECATED_API EXTERNC __declspec(deprecated) #define YR_DEPRECATED __declspec(deprecated) #endif #else #define YR_API EXTERNC #define YR_DEPRECATED_API EXTERNC #define YR_DEPRECATED #endif #else #if __GNUC__ >= 4 #define YR_API EXTERNC __attribute__((visibility("default"))) #define YR_DEPRECATED_API YR_API __attribute__((deprecated)) #define YR_DEPRECATED __attribute__((deprecated)) #else #define YR_API EXTERNC #define YR_DEPRECATED_API EXTERNC #define YR_DEPRECATED #endif #endif #if defined(__GNUC__) #define YR_ALIGN(n) __attribute__((aligned(n))) #elif defined(_MSC_VER) #define YR_ALIGN(n) __declspec(align(n)) #else #define YR_ALIGN(n) #endif #if defined(__GNUC__) #define YR_PRINTF_LIKE(x, y) __attribute__((format(printf, x, y))) #else #define YR_PRINTF_LIKE(x, y) #endif #define yr_min(x, y) (((x) < (y)) ? (x) : (y)) #define yr_max(x, y) (((x) > (y)) ? (x) : (y)) #define yr_swap(x, y, T) \ do \ { \ T temp = x; \ x = y; \ y = temp; \ } while (0) #ifdef NDEBUG #define assertf(expr, msg, ...) ((void) 0) #else #include #define assertf(expr, msg, ...) \ if (!(expr)) \ { \ fprintf(stderr, "%s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ abort(); \ } #endif // Set, unset, and test bits in an array of unsigned characters by integer // index. The underlying array must be of type char or unsigned char to // ensure compatibility with the CHAR_BIT constant used in these definitions. #define YR_BITARRAY_SET(uchar_array_base, bitnum) \ (((uchar_array_base)[(bitnum) / CHAR_BIT]) = \ ((uchar_array_base)[(bitnum) / CHAR_BIT] | \ (1 << ((bitnum) % CHAR_BIT)))) #define YR_BITARRAY_UNSET(uchar_array_base, bitnum) \ (((uchar_array_base)[(bitnum) / CHAR_BIT]) = \ ((uchar_array_base)[(bitnum) / CHAR_BIT] & \ (~(1 << ((bitnum) % CHAR_BIT))))) #define YR_BITARRAY_TEST(uchar_array_base, bitnum) \ (((uchar_array_base)[(bitnum) / CHAR_BIT] & (1 << ((bitnum) % CHAR_BIT))) != \ 0) #define YR_BITARRAY_NCHARS(bitnum) (((bitnum) + (CHAR_BIT - 1)) / CHAR_BIT) #endif yara-4.1.3/libyara/lexer.c000066400000000000000000003001501413423160300153550ustar00rootroot00000000000000#line 1 "lexer.c" #line 3 "lexer.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif #ifdef yy_create_buffer #define yara_yy_create_buffer_ALREADY_DEFINED #else #define yy_create_buffer yara_yy_create_buffer #endif #ifdef yy_delete_buffer #define yara_yy_delete_buffer_ALREADY_DEFINED #else #define yy_delete_buffer yara_yy_delete_buffer #endif #ifdef yy_scan_buffer #define yara_yy_scan_buffer_ALREADY_DEFINED #else #define yy_scan_buffer yara_yy_scan_buffer #endif #ifdef yy_scan_string #define yara_yy_scan_string_ALREADY_DEFINED #else #define yy_scan_string yara_yy_scan_string #endif #ifdef yy_scan_bytes #define yara_yy_scan_bytes_ALREADY_DEFINED #else #define yy_scan_bytes yara_yy_scan_bytes #endif #ifdef yy_init_buffer #define yara_yy_init_buffer_ALREADY_DEFINED #else #define yy_init_buffer yara_yy_init_buffer #endif #ifdef yy_flush_buffer #define yara_yy_flush_buffer_ALREADY_DEFINED #else #define yy_flush_buffer yara_yy_flush_buffer #endif #ifdef yy_load_buffer_state #define yara_yy_load_buffer_state_ALREADY_DEFINED #else #define yy_load_buffer_state yara_yy_load_buffer_state #endif #ifdef yy_switch_to_buffer #define yara_yy_switch_to_buffer_ALREADY_DEFINED #else #define yy_switch_to_buffer yara_yy_switch_to_buffer #endif #ifdef yypush_buffer_state #define yara_yypush_buffer_state_ALREADY_DEFINED #else #define yypush_buffer_state yara_yypush_buffer_state #endif #ifdef yypop_buffer_state #define yara_yypop_buffer_state_ALREADY_DEFINED #else #define yypop_buffer_state yara_yypop_buffer_state #endif #ifdef yyensure_buffer_stack #define yara_yyensure_buffer_stack_ALREADY_DEFINED #else #define yyensure_buffer_stack yara_yyensure_buffer_stack #endif #ifdef yylex #define yara_yylex_ALREADY_DEFINED #else #define yylex yara_yylex #endif #ifdef yyrestart #define yara_yyrestart_ALREADY_DEFINED #else #define yyrestart yara_yyrestart #endif #ifdef yylex_init #define yara_yylex_init_ALREADY_DEFINED #else #define yylex_init yara_yylex_init #endif #ifdef yylex_init_extra #define yara_yylex_init_extra_ALREADY_DEFINED #else #define yylex_init_extra yara_yylex_init_extra #endif #ifdef yylex_destroy #define yara_yylex_destroy_ALREADY_DEFINED #else #define yylex_destroy yara_yylex_destroy #endif #ifdef yyget_debug #define yara_yyget_debug_ALREADY_DEFINED #else #define yyget_debug yara_yyget_debug #endif #ifdef yyset_debug #define yara_yyset_debug_ALREADY_DEFINED #else #define yyset_debug yara_yyset_debug #endif #ifdef yyget_extra #define yara_yyget_extra_ALREADY_DEFINED #else #define yyget_extra yara_yyget_extra #endif #ifdef yyset_extra #define yara_yyset_extra_ALREADY_DEFINED #else #define yyset_extra yara_yyset_extra #endif #ifdef yyget_in #define yara_yyget_in_ALREADY_DEFINED #else #define yyget_in yara_yyget_in #endif #ifdef yyset_in #define yara_yyset_in_ALREADY_DEFINED #else #define yyset_in yara_yyset_in #endif #ifdef yyget_out #define yara_yyget_out_ALREADY_DEFINED #else #define yyget_out yara_yyget_out #endif #ifdef yyset_out #define yara_yyset_out_ALREADY_DEFINED #else #define yyset_out yara_yyset_out #endif #ifdef yyget_leng #define yara_yyget_leng_ALREADY_DEFINED #else #define yyget_leng yara_yyget_leng #endif #ifdef yyget_text #define yara_yyget_text_ALREADY_DEFINED #else #define yyget_text yara_yyget_text #endif #ifdef yyget_lineno #define yara_yyget_lineno_ALREADY_DEFINED #else #define yyget_lineno yara_yyget_lineno #endif #ifdef yyset_lineno #define yara_yyset_lineno_ALREADY_DEFINED #else #define yyset_lineno yara_yyset_lineno #endif #ifdef yyget_column #define yara_yyget_column_ALREADY_DEFINED #else #define yyget_column yara_yyget_column #endif #ifdef yyset_column #define yara_yyset_column_ALREADY_DEFINED #else #define yyset_column yara_yyset_column #endif #ifdef yywrap #define yara_yywrap_ALREADY_DEFINED #else #define yywrap yara_yywrap #endif #ifdef yyget_lval #define yara_yyget_lval_ALREADY_DEFINED #else #define yyget_lval yara_yyget_lval #endif #ifdef yyset_lval #define yara_yyset_lval_ALREADY_DEFINED #else #define yyset_lval yara_yyset_lval #endif #ifdef yyalloc #define yara_yyalloc_ALREADY_DEFINED #else #define yyalloc yara_yyalloc #endif #ifdef yyrealloc #define yara_yyrealloc_ALREADY_DEFINED #else #define yyrealloc yara_yyrealloc #endif #ifdef yyfree #define yara_yyfree_ALREADY_DEFINED #else #define yyfree yara_yyfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an * integer in range [0..255] for use as an array index. */ #define YY_SC_TO_UI(c) ((YY_CHAR) (c)) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) #define YY_LINENO_REWIND_TO(dst) \ do {\ const char *p;\ for ( p = yy_cp-1; p >= (dst); --p)\ if ( *p == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void yyrestart ( FILE *input_file , yyscan_t yyscanner ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); void yypop_buffer_state ( yyscan_t yyscanner ); static void yyensure_buffer_stack ( yyscan_t yyscanner ); static void yy_load_buffer_state ( yyscan_t yyscanner ); static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); #define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); void yyfree ( void * , yyscan_t yyscanner ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define yara_yywrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); static int yy_get_next_buffer ( yyscan_t yyscanner ); static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 83 #define YY_END_OF_BUFFER 84 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static const flex_int16_t yy_accept[299] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 82, 81, 81, 56, 78, 54, 53, 82, 79, 59, 59, 2, 82, 3, 55, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 82, 70, 71, 63, 83, 76, 77, 73, 83, 50, 51, 47, 47, 56, 7, 54, 52, 53, 1, 45, 48, 0, 59, 0, 0, 0, 0, 8, 4, 6, 5, 9, 55, 58, 58, 58, 58, 28, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 29, 58, 58, 58, 58, 30, 27, 58, 58, 58, 58, 58, 58, 58, 58, 0, 0, 70, 72, 67, 68, 66, 65, 64, 72, 76, 73, 73, 75, 74, 50, 46, 48, 60, 59, 62, 61, 33, 26, 34, 58, 58, 58, 58, 58, 58, 58, 32, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 25, 58, 58, 58, 58, 58, 58, 58, 58, 17, 80, 0, 0, 0, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 57, 58, 58, 13, 58, 58, 12, 58, 58, 31, 23, 16, 0, 0, 0, 0, 0, 80, 69, 15, 58, 58, 58, 58, 58, 24, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 0, 0, 18, 58, 58, 58, 58, 58, 58, 11, 58, 58, 44, 58, 57, 58, 58, 21, 58, 58, 58, 0, 0, 0, 0, 0, 80, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 37, 10, 58, 14, 0, 80, 0, 0, 0, 58, 58, 38, 40, 58, 36, 20, 58, 58, 0, 58, 58, 0, 0, 0, 58, 22, 58, 41, 43, 49, 58, 58, 19, 35, 58, 39, 42, 0 } ; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 5, 6, 7, 8, 1, 1, 1, 9, 9, 10, 1, 1, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 18, 20, 21, 1, 1, 22, 23, 24, 9, 25, 26, 27, 26, 26, 26, 26, 28, 28, 28, 28, 29, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 9, 31, 9, 1, 32, 1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 28, 28, 42, 43, 44, 45, 46, 28, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 9, 57, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const YY_CHAR yy_meta[58] = { 0, 1, 2, 3, 2, 1, 4, 1, 1, 2, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 9, 1, 1, 10, 10, 11, 12, 12, 13, 11, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 1, 1 } ; static const flex_int16_t yy_base[321] = { 0, 0, 0, 55, 56, 57, 60, 628, 627, 622, 621, 630, 635, 635, 635, 606, 635, 0, 618, 616, 54, 54, 60, 45, 603, 50, 0, 0, 33, 592, 579, 579, 52, 580, 66, 43, 576, 32, 573, 569, 569, 64, 576, 575, 570, 602, 0, 635, 635, 86, 0, 635, 67, 601, 0, 635, 635, 600, 588, 635, 0, 635, 600, 635, 635, 0, 0, 0, 582, 581, 105, 0, 635, 635, 635, 635, 635, 0, 0, 565, 42, 571, 0, 557, 560, 76, 561, 560, 554, 558, 554, 553, 553, 550, 91, 546, 545, 544, 92, 0, 0, 551, 549, 95, 553, 539, 544, 551, 539, 86, 119, 0, 635, 635, 635, 635, 635, 635, 0, 0, 537, 635, 635, 635, 0, 635, 0, 0, 635, 131, 0, 0, 0, 0, 543, 546, 102, 534, 534, 532, 542, 0, 536, 543, 532, 539, 529, 531, 138, 539, 536, 537, 536, 0, 517, 530, 519, 524, 521, 526, 513, 524, 0, 635, 550, 158, 0, 518, 539, 516, 523, 503, 500, 516, 504, 499, 517, 500, 500, 500, 496, 526, 529, 509, 495, 501, 0, 492, 506, 0, 489, 493, 0, 0, 0, 526, 122, 0, 0, 143, 0, 635, 0, 518, 485, 492, 491, 485, 0, 489, 484, 486, 494, 474, 475, 472, 468, 444, 444, 430, 410, 407, 392, 215, 263, 378, 385, 379, 373, 376, 360, 363, 0, 366, 365, 0, 368, 0, 355, 350, 0, 359, 343, 342, 0, 0, 274, 318, 269, 0, 288, 283, 279, 286, 284, 287, 287, 278, 272, 318, 267, 0, 0, 277, 0, 270, 307, 286, 304, 287, 278, 260, 0, 0, 241, 0, 0, 226, 117, 300, 115, 88, 293, 0, 303, 99, 0, 67, 0, 0, 635, 57, 60, 0, 0, 55, 0, 0, 635, 331, 344, 357, 370, 376, 381, 389, 396, 401, 406, 417, 427, 439, 452, 464, 477, 490, 86, 496, 499, 509, 515 } ; static const flex_int16_t yy_def[321] = { 0, 298, 1, 299, 299, 300, 300, 301, 301, 302, 302, 298, 298, 298, 298, 303, 298, 304, 305, 298, 298, 306, 306, 298, 298, 298, 307, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 309, 310, 298, 298, 311, 312, 298, 298, 313, 314, 298, 298, 298, 303, 298, 304, 298, 305, 298, 298, 315, 316, 22, 298, 298, 298, 317, 298, 298, 298, 298, 298, 307, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 309, 298, 310, 298, 298, 298, 298, 298, 298, 318, 312, 298, 298, 298, 298, 314, 298, 315, 316, 298, 298, 317, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 298, 319, 298, 320, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 319, 319, 165, 165, 165, 165, 298, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 298, 165, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 223, 223, 319, 223, 223, 223, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, 319, 319, 165, 223, 223, 308, 308, 308, 308, 308, 308, 308, 308, 308, 298, 308, 308, 319, 223, 223, 308, 308, 308, 308, 308, 298, 308, 308, 308, 308, 308, 308, 308, 0, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298 } ; static const flex_int16_t yy_nxt[693] = { 0, 12, 13, 14, 13, 15, 16, 17, 18, 12, 12, 19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 24, 25, 26, 27, 27, 27, 27, 27, 12, 27, 28, 29, 30, 27, 31, 32, 33, 27, 34, 27, 35, 36, 37, 38, 39, 40, 41, 42, 27, 43, 44, 27, 27, 45, 12, 47, 47, 51, 48, 48, 51, 64, 66, 65, 72, 73, 52, 99, 66, 52, 75, 76, 79, 96, 80, 132, 100, 97, 81, 82, 68, 69, 86, 49, 49, 53, 68, 69, 53, 113, 87, 127, 297, 133, 88, 110, 70, 296, 91, 89, 92, 104, 298, 295, 71, 120, 93, 94, 105, 137, 298, 95, 121, 294, 114, 129, 129, 129, 129, 129, 129, 129, 138, 147, 152, 156, 164, 115, 165, 196, 116, 109, 117, 293, 292, 169, 118, 148, 153, 157, 163, 129, 129, 129, 129, 129, 129, 129, 170, 181, 223, 182, 224, 291, 289, 183, 197, 198, 109, 198, 197, 197, 197, 197, 198, 197, 197, 199, 198, 198, 198, 198, 198, 198, 198, 198, 198, 197, 197, 197, 197, 198, 198, 197, 197, 197, 197, 197, 198, 198, 198, 198, 198, 198, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 197, 200, 244, 245, 246, 245, 244, 244, 244, 244, 245, 247, 244, 248, 245, 245, 245, 245, 245, 245, 245, 245, 245, 244, 244, 244, 244, 245, 245, 244, 244, 244, 244, 244, 245, 245, 245, 245, 245, 245, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 249, 223, 288, 224, 246, 246, 246, 268, 282, 269, 283, 246, 196, 287, 265, 246, 246, 246, 246, 246, 246, 246, 246, 246, 223, 268, 224, 269, 246, 246, 279, 196, 286, 246, 290, 246, 246, 246, 246, 246, 246, 268, 285, 269, 284, 196, 281, 280, 279, 278, 277, 276, 275, 274, 273, 272, 271, 270, 267, 266, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 58, 58, 58, 58, 58, 60, 264, 60, 60, 60, 62, 263, 262, 62, 261, 62, 62, 62, 67, 260, 67, 259, 258, 257, 67, 77, 256, 77, 77, 77, 78, 255, 78, 78, 78, 109, 109, 254, 253, 252, 109, 109, 251, 109, 111, 111, 250, 243, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 119, 119, 242, 119, 119, 119, 241, 119, 119, 119, 119, 119, 122, 122, 240, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 124, 124, 124, 239, 124, 124, 124, 124, 124, 124, 124, 124, 124, 126, 126, 238, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 130, 237, 130, 166, 236, 166, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 201, 235, 201, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 196, 222, 221, 220, 219, 218, 217, 216, 183, 183, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 196, 194, 148, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 168, 167, 121, 162, 161, 160, 159, 158, 155, 154, 151, 150, 149, 146, 145, 144, 143, 142, 141, 140, 139, 136, 135, 134, 131, 128, 128, 61, 298, 125, 123, 110, 108, 107, 106, 103, 102, 101, 98, 90, 85, 84, 83, 74, 63, 61, 59, 298, 57, 57, 55, 55, 11, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298 } ; static const flex_int16_t yy_chk[693] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, 5, 3, 4, 6, 20, 21, 20, 23, 23, 5, 37, 22, 6, 25, 25, 28, 35, 28, 80, 37, 35, 28, 28, 21, 21, 32, 3, 4, 5, 22, 22, 6, 49, 32, 316, 295, 80, 32, 109, 21, 292, 34, 32, 34, 41, 22, 291, 21, 52, 34, 34, 41, 85, 22, 34, 52, 287, 49, 70, 70, 70, 70, 70, 70, 70, 85, 94, 98, 103, 110, 49, 110, 196, 49, 196, 49, 285, 281, 136, 49, 94, 98, 103, 109, 129, 129, 129, 129, 129, 129, 129, 136, 148, 199, 148, 199, 280, 278, 148, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 223, 224, 277, 224, 246, 246, 246, 248, 265, 248, 265, 246, 246, 274, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 267, 269, 267, 269, 246, 246, 279, 282, 271, 282, 279, 246, 246, 246, 246, 246, 246, 284, 270, 284, 268, 266, 263, 260, 259, 258, 257, 256, 255, 254, 253, 252, 251, 250, 247, 246, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 299, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, 301, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 303, 303, 303, 303, 303, 304, 243, 304, 304, 304, 305, 242, 241, 305, 239, 305, 305, 305, 306, 238, 306, 236, 234, 233, 306, 307, 231, 307, 307, 307, 308, 230, 308, 308, 308, 309, 309, 229, 228, 227, 309, 309, 226, 309, 310, 310, 225, 222, 310, 310, 310, 310, 310, 310, 310, 310, 311, 311, 311, 311, 311, 311, 311, 311, 311, 311, 311, 311, 311, 312, 312, 221, 312, 312, 312, 220, 312, 312, 312, 312, 312, 313, 313, 219, 313, 313, 313, 313, 313, 313, 313, 313, 313, 313, 314, 314, 314, 218, 314, 314, 314, 314, 314, 314, 314, 314, 314, 315, 315, 217, 315, 315, 315, 315, 315, 315, 315, 315, 315, 315, 317, 216, 317, 318, 215, 318, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, 320, 214, 320, 213, 212, 211, 210, 209, 207, 206, 205, 204, 203, 195, 191, 190, 188, 187, 185, 184, 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 164, 161, 160, 159, 158, 157, 156, 155, 154, 152, 151, 150, 149, 147, 146, 145, 144, 143, 142, 140, 139, 138, 137, 135, 134, 120, 108, 107, 106, 105, 104, 102, 101, 97, 96, 95, 93, 92, 91, 90, 89, 88, 87, 86, 84, 83, 81, 79, 69, 68, 62, 58, 57, 53, 45, 44, 43, 42, 40, 39, 38, 36, 33, 31, 30, 29, 24, 19, 18, 15, 11, 10, 9, 8, 7, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298 } ; /* Table of booleans, true if rule could match eol. */ static const flex_int32_t yy_rule_can_match_eol[84] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, }; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "lexer.l" /* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Lexical analyzer for YARA */ #line 33 "lexer.l" /* Disable warnings for unused functions in this file. As we redefine YY_FATAL_ERROR macro to use our own function yara_yyfatal, the yy_fatal_error function generated by Flex is not actually used, causing a compiler warning. Flex doesn't offer any options to remove the yy_fatal_error function. When they include something like %option noyy_fatal_error as they do with noyywrap then we can remove this pragma. */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #include #include #include #if defined(_WIN32) || defined(__CYGWIN__) #include #else #include #include #endif #if defined(_WIN32) #define strtoll _strtoi64 #endif #include #include #include #include #include #include #include #include "grammar.h" #define error(error_code) \ { \ compiler->last_error = error_code; \ yyerror(yyscanner, compiler, NULL); \ yyterminate(); \ } #define syntax_error(error_msg) \ { \ yr_compiler_set_error_extra_info(compiler, error_msg); \ error(ERROR_SYNTAX_ERROR); \ } #define lex_check_space_ok(data, current_size, max_length) \ if (strlen(data) + current_size >= max_length - 1) \ { \ yyerror(yyscanner, compiler, "out of space in lex_buf"); \ yyterminate(); \ } #define yytext_to_buffer \ { \ char *yptr = yytext; \ lex_check_space_ok(yptr, yyextra->lex_buf_len, YR_LEX_BUF_SIZE); \ while(*yptr) \ { \ *yyextra->lex_buf_ptr++ = *yptr++; \ yyextra->lex_buf_len++; \ } \ } #define alloc_sized_string(str, str_len) \ SIZED_STRING* str = (SIZED_STRING*) yr_malloc( \ str_len + sizeof(SIZED_STRING)); \ if (str == NULL) \ { \ yyerror(yyscanner, compiler, "not enough memory"); \ yyterminate(); \ } \ else \ { \ str->length = (uint32_t) (str_len); \ str->flags = 0; \ } \ #ifdef _WIN32 #define snprintf _snprintf #endif static bool is_absolute_path( char* path) { if (path == NULL) return false; #if defined(_WIN32) || defined(__CYGWIN__) return strlen(path) > 2 && path[1] == ':' && (path[2] == '/' || path[2] == '\\'); #else return strlen(path) > 0 && path[0] == '/'; #endif } #line 1063 "lexer.c" #define YY_NO_UNISTD_H 1 #define YY_NO_INPUT 1 #line 1067 "lexer.c" #define INITIAL 0 #define str 1 #define regexp 2 #define include 3 #define comment 4 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; }; /* end struct yyguts_t */ static int yy_init_globals ( yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r int yylex_init (yyscan_t* scanner); int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy ( yyscan_t yyscanner ); int yyget_debug ( yyscan_t yyscanner ); void yyset_debug ( int debug_flag , yyscan_t yyscanner ); YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); FILE *yyget_in ( yyscan_t yyscanner ); void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); int yyget_lineno ( yyscan_t yyscanner ); void yyset_lineno ( int _line_number , yyscan_t yyscanner ); int yyget_column ( yyscan_t yyscanner ); void yyset_column ( int _column_no , yyscan_t yyscanner ); YYSTYPE * yyget_lval ( yyscan_t yyscanner ); void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap ( yyscan_t yyscanner ); #else extern int yywrap ( yyscan_t yyscanner ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput ( yyscan_t yyscanner ); #else static int input ( yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner); #define YY_DECL int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_load_buffer_state( yyscanner ); } { #line 163 "lexer.l" #line 1346 "lexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 299 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } while ( yy_current_state != 298 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; for ( yyl = 0; yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 165 "lexer.l" { return _DOT_DOT_; } YY_BREAK case 2: YY_RULE_SETUP #line 166 "lexer.l" { return _LT_; } YY_BREAK case 3: YY_RULE_SETUP #line 167 "lexer.l" { return _GT_; } YY_BREAK case 4: YY_RULE_SETUP #line 168 "lexer.l" { return _LE_; } YY_BREAK case 5: YY_RULE_SETUP #line 169 "lexer.l" { return _GE_; } YY_BREAK case 6: YY_RULE_SETUP #line 170 "lexer.l" { return _EQ_; } YY_BREAK case 7: YY_RULE_SETUP #line 171 "lexer.l" { return _NEQ_; } YY_BREAK case 8: YY_RULE_SETUP #line 172 "lexer.l" { return _SHIFT_LEFT_; } YY_BREAK case 9: YY_RULE_SETUP #line 173 "lexer.l" { return _SHIFT_RIGHT_; } YY_BREAK case 10: YY_RULE_SETUP #line 174 "lexer.l" { return _PRIVATE_; } YY_BREAK case 11: YY_RULE_SETUP #line 175 "lexer.l" { return _GLOBAL_; } YY_BREAK case 12: YY_RULE_SETUP #line 176 "lexer.l" { return _RULE_; } YY_BREAK case 13: YY_RULE_SETUP #line 177 "lexer.l" { return _META_; } YY_BREAK case 14: YY_RULE_SETUP #line 178 "lexer.l" { return _STRINGS_; } YY_BREAK case 15: YY_RULE_SETUP #line 179 "lexer.l" { return _ASCII_; } YY_BREAK case 16: YY_RULE_SETUP #line 180 "lexer.l" { return _WIDE_; } YY_BREAK case 17: YY_RULE_SETUP #line 181 "lexer.l" { return _XOR_; } YY_BREAK case 18: YY_RULE_SETUP #line 182 "lexer.l" { return _BASE64_; } YY_BREAK case 19: YY_RULE_SETUP #line 183 "lexer.l" { return _BASE64_WIDE_; } YY_BREAK case 20: YY_RULE_SETUP #line 184 "lexer.l" { return _FULLWORD_; } YY_BREAK case 21: YY_RULE_SETUP #line 185 "lexer.l" { return _NOCASE_; } YY_BREAK case 22: YY_RULE_SETUP #line 186 "lexer.l" { return _CONDITION_; } YY_BREAK case 23: YY_RULE_SETUP #line 187 "lexer.l" { return _TRUE_; } YY_BREAK case 24: YY_RULE_SETUP #line 188 "lexer.l" { return _FALSE_; } YY_BREAK case 25: YY_RULE_SETUP #line 189 "lexer.l" { return _NOT_; } YY_BREAK case 26: YY_RULE_SETUP #line 190 "lexer.l" { return _AND_; } YY_BREAK case 27: YY_RULE_SETUP #line 191 "lexer.l" { return _OR_; } YY_BREAK case 28: YY_RULE_SETUP #line 192 "lexer.l" { return _AT_; } YY_BREAK case 29: YY_RULE_SETUP #line 193 "lexer.l" { return _IN_; } YY_BREAK case 30: YY_RULE_SETUP #line 194 "lexer.l" { return _OF_; } YY_BREAK case 31: YY_RULE_SETUP #line 195 "lexer.l" { return _THEM_; } YY_BREAK case 32: YY_RULE_SETUP #line 196 "lexer.l" { return _FOR_; } YY_BREAK case 33: YY_RULE_SETUP #line 197 "lexer.l" { return _ALL_; } YY_BREAK case 34: YY_RULE_SETUP #line 198 "lexer.l" { return _ANY_; } YY_BREAK case 35: YY_RULE_SETUP #line 199 "lexer.l" { return _ENTRYPOINT_; } YY_BREAK case 36: YY_RULE_SETUP #line 200 "lexer.l" { return _FILESIZE_; } YY_BREAK case 37: YY_RULE_SETUP #line 201 "lexer.l" { return _MATCHES_; } YY_BREAK case 38: YY_RULE_SETUP #line 202 "lexer.l" { return _CONTAINS_; } YY_BREAK case 39: YY_RULE_SETUP #line 203 "lexer.l" { return _STARTSWITH_; } YY_BREAK case 40: YY_RULE_SETUP #line 204 "lexer.l" { return _ENDSWITH_; } YY_BREAK case 41: YY_RULE_SETUP #line 205 "lexer.l" { return _ICONTAINS_; } YY_BREAK case 42: YY_RULE_SETUP #line 206 "lexer.l" { return _ISTARTSWITH_; } YY_BREAK case 43: YY_RULE_SETUP #line 207 "lexer.l" { return _IENDSWITH_; } YY_BREAK case 44: YY_RULE_SETUP #line 208 "lexer.l" { return _IMPORT_; } YY_BREAK case 45: YY_RULE_SETUP #line 211 "lexer.l" { BEGIN(comment); } YY_BREAK case 46: YY_RULE_SETUP #line 212 "lexer.l" { BEGIN(INITIAL); } YY_BREAK case 47: /* rule 47 can match eol */ YY_RULE_SETUP #line 213 "lexer.l" { /* skip comments */ } YY_BREAK case 48: YY_RULE_SETUP #line 216 "lexer.l" { /* skip single-line comments */ } YY_BREAK case 49: YY_RULE_SETUP #line 219 "lexer.l" { yyextra->lex_buf_ptr = yyextra->lex_buf; yyextra->lex_buf_len = 0; BEGIN(include); } YY_BREAK case 50: /* rule 50 can match eol */ YY_RULE_SETUP #line 226 "lexer.l" { yytext_to_buffer; } YY_BREAK case 51: YY_RULE_SETUP #line 229 "lexer.l" { if (compiler->include_callback != NULL) { #ifdef _MSC_VER char* b = NULL; #endif char* s = NULL; char* f; char buffer[1024]; const char* included_rules; char* current_file_name; char* include_path; *yyextra->lex_buf_ptr = '\0'; // null-terminate included file path current_file_name = yr_compiler_get_current_file_name(compiler); if (current_file_name == NULL || compiler->include_callback != _yr_compiler_default_include_callback || is_absolute_path(yyextra->lex_buf)) { include_path = yyextra->lex_buf; } else { strlcpy(buffer, current_file_name, sizeof(buffer)); s = strrchr(buffer, '/'); #ifdef _MSC_VER b = strrchr(buffer, '\\'); // in Windows both path delimiters are accepted #endif #ifdef _MSC_VER if (s != NULL || b != NULL) #else if (s != NULL) #endif { #ifdef _MSC_VER f = (b > s) ? (b + 1) : (s + 1); #else f = s + 1; #endif strlcpy(f, yyextra->lex_buf, sizeof(buffer) - (f - buffer)); include_path = buffer; } else { include_path = yyextra->lex_buf; } } YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE)); included_rules = compiler->include_callback( include_path, current_file_name, ns->name, compiler->incl_clbk_user_data); if (included_rules != NULL) { int error_code = _yr_compiler_push_file_name(compiler, include_path); if (error_code != ERROR_SUCCESS) { if (error_code == ERROR_INCLUDES_CIRCULAR_REFERENCE) { yyerror(yyscanner, compiler, "includes circular reference"); } else if (error_code == ERROR_INCLUDE_DEPTH_EXCEEDED) { yyerror(yyscanner, compiler, "includes depth exceeded"); } if (compiler->include_free != NULL) { compiler->include_free(included_rules, compiler->incl_clbk_user_data); } yyterminate(); } // Workaround for flex issue: https://github.com/westes/flex/issues/58 yypush_buffer_state(YY_CURRENT_BUFFER, yyscanner); yy_scan_string(included_rules, yyscanner); yyset_lineno(1, yyscanner); if (compiler->include_free != NULL) { compiler->include_free(included_rules, compiler->incl_clbk_user_data); } } else { char* err_msg_fmt; char err_msg[512]; if (compiler->include_callback == _yr_compiler_default_include_callback) { err_msg_fmt = "can't open include file: %s"; } else { err_msg_fmt = "callback failed to provide include resource: %s"; } snprintf( err_msg, sizeof(err_msg), err_msg_fmt, yyextra->lex_buf); yyerror(yyscanner, compiler, err_msg); } } else // not allowing includes { yyerror(yyscanner, compiler, "includes are disabled"); } BEGIN(INITIAL); } YY_BREAK case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(str): case YY_STATE_EOF(regexp): case YY_STATE_EOF(include): case YY_STATE_EOF(comment): #line 361 "lexer.l" { yypop_buffer_state(yyscanner); if (!YY_CURRENT_BUFFER) yyterminate(); return _END_OF_INCLUDED_FILE_; } YY_BREAK case 52: YY_RULE_SETUP #line 372 "lexer.l" { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) error(ERROR_INSUFFICIENT_MEMORY); return _STRING_IDENTIFIER_WITH_WILDCARD_; } YY_BREAK case 53: YY_RULE_SETUP #line 383 "lexer.l" { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) error(ERROR_INSUFFICIENT_MEMORY); return _STRING_IDENTIFIER_; } YY_BREAK case 54: YY_RULE_SETUP #line 394 "lexer.l" { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) { error(ERROR_INSUFFICIENT_MEMORY); } else { yylval->c_string[0] = '$'; /* replace # by $*/ } return _STRING_COUNT_; } YY_BREAK case 55: YY_RULE_SETUP #line 411 "lexer.l" { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) { error(ERROR_INSUFFICIENT_MEMORY); } else { yylval->c_string[0] = '$'; /* replace @ by $*/ } return _STRING_OFFSET_; } YY_BREAK case 56: YY_RULE_SETUP #line 428 "lexer.l" { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) { error(ERROR_INSUFFICIENT_MEMORY); } else { yylval->c_string[0] = '$'; /* replace ! by $*/ } return _STRING_LENGTH_; } YY_BREAK case 57: YY_RULE_SETUP #line 445 "lexer.l" { char* text = yytext; if (*text == 'u') { yylval->integer = 3; text++; } else { yylval->integer = 0; } if (strstr(text, "int8") == text) { yylval->integer += 0; text += 4; } else if (strstr(text, "int16") == text) { yylval->integer += 1; text += 5; } else if (strstr(text, "int32") == text) { yylval->integer += 2; text += 5; } if (strcmp(text, "be") == 0) { yylval->integer += 6; } return _INTEGER_FUNCTION_; } YY_BREAK case 58: YY_RULE_SETUP #line 484 "lexer.l" { if (strlen(yytext) > 128) syntax_error("identifier too long"); yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) error(ERROR_INSUFFICIENT_MEMORY); return _IDENTIFIER_; } YY_BREAK case 59: YY_RULE_SETUP #line 498 "lexer.l" { char *endptr; errno = 0; yylval->integer = strtoll(yytext, &endptr, 10); if (yylval->integer == LLONG_MAX && errno == ERANGE) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } else if (strstr(yytext, "KB") != NULL) { if (yylval->integer > LLONG_MAX / 1024) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } else { yylval->integer *= 1024; } } else if (strstr(yytext, "MB") != NULL) { if (yylval->integer > LLONG_MAX / 1048576) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } else { yylval->integer *= 1048576; } } return _NUMBER_; } YY_BREAK case 60: YY_RULE_SETUP #line 538 "lexer.l" { yylval->double_ = atof(yytext); return _DOUBLE_; } YY_BREAK case 61: YY_RULE_SETUP #line 543 "lexer.l" { char *endptr; errno = 0; yylval->integer = strtoll(yytext, &endptr, 16); if (yylval->integer == LLONG_MAX && errno == ERANGE) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } return _NUMBER_; } YY_BREAK case 62: YY_RULE_SETUP #line 559 "lexer.l" { char *endptr; errno = 0; yylval->integer = strtoll(yytext + 2, &endptr, 8); if (yylval->integer == LLONG_MAX && errno == ERANGE) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } return _NUMBER_; } YY_BREAK case 63: YY_RULE_SETUP #line 576 "lexer.l" { /* saw closing quote - all done */ alloc_sized_string(s, yyextra->lex_buf_len); *yyextra->lex_buf_ptr = '\0'; memcpy(s->c_string, yyextra->lex_buf, yyextra->lex_buf_len + 1); yylval->sized_string = s; BEGIN(INITIAL); return _TEXT_STRING_; } YY_BREAK case 64: YY_RULE_SETUP #line 591 "lexer.l" { lex_check_space_ok("\t", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\t'; yyextra->lex_buf_len++; } YY_BREAK case 65: YY_RULE_SETUP #line 599 "lexer.l" { lex_check_space_ok("\r", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\r'; yyextra->lex_buf_len++; } YY_BREAK case 66: YY_RULE_SETUP #line 607 "lexer.l" { lex_check_space_ok("\n", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\n'; yyextra->lex_buf_len++; } YY_BREAK case 67: YY_RULE_SETUP #line 615 "lexer.l" { lex_check_space_ok("\"", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\"'; yyextra->lex_buf_len++; } YY_BREAK case 68: YY_RULE_SETUP #line 623 "lexer.l" { lex_check_space_ok("\\", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\\'; yyextra->lex_buf_len++; } YY_BREAK case 69: YY_RULE_SETUP #line 631 "lexer.l" { int result; sscanf(yytext + 2, "%x", &result); lex_check_space_ok("X", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = result; yyextra->lex_buf_len++; } YY_BREAK case 70: YY_RULE_SETUP #line 642 "lexer.l" { yytext_to_buffer; } YY_BREAK case 71: /* rule 71 can match eol */ YY_RULE_SETUP #line 645 "lexer.l" { syntax_error("unterminated string"); } YY_BREAK case 72: /* rule 72 can match eol */ YY_RULE_SETUP #line 650 "lexer.l" { syntax_error("illegal escape sequence"); } YY_BREAK case 73: YY_RULE_SETUP #line 655 "lexer.l" { if (yyextra->lex_buf_len > 0) { alloc_sized_string(s, yyextra->lex_buf_len); if (yytext[1] == 'i') s->flags |= SIZED_STRING_FLAGS_NO_CASE; if (yytext[1] == 's' || yytext[2] == 's') s->flags |= SIZED_STRING_FLAGS_DOT_ALL; *yyextra->lex_buf_ptr = '\0'; strlcpy(s->c_string, yyextra->lex_buf, s->length + 1); yylval->sized_string = s; } else { syntax_error("empty regular expression"); } BEGIN(INITIAL); return _REGEXP_; } YY_BREAK case 74: YY_RULE_SETUP #line 682 "lexer.l" { lex_check_space_ok("/", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '/'; yyextra->lex_buf_len++ ; } YY_BREAK case 75: YY_RULE_SETUP #line 690 "lexer.l" { lex_check_space_ok("\\.", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); if (yytext[1] == 0) syntax_error("malformed regular expression"); *yyextra->lex_buf_ptr++ = yytext[0]; *yyextra->lex_buf_ptr++ = yytext[1]; yyextra->lex_buf_len += 2; } YY_BREAK case 76: YY_RULE_SETUP #line 703 "lexer.l" { yytext_to_buffer; } YY_BREAK case 77: /* rule 77 can match eol */ YY_RULE_SETUP #line 706 "lexer.l" { syntax_error("unterminated regular expression"); } YY_BREAK case 78: YY_RULE_SETUP #line 711 "lexer.l" { yylval->sized_string = NULL; yyextra->lex_buf_ptr = yyextra->lex_buf; yyextra->lex_buf_len = 0; BEGIN(str); } YY_BREAK case 79: YY_RULE_SETUP #line 720 "lexer.l" { yylval->sized_string = NULL; yyextra->lex_buf_ptr = yyextra->lex_buf; yyextra->lex_buf_len = 0; BEGIN(regexp); } YY_BREAK case 80: /* rule 80 can match eol */ YY_RULE_SETUP #line 729 "lexer.l" { // Match hex-digits with whitespace or comments. The latter are stripped // out by hex_lexer.l // TODO(vmalvarez): Integrate the hex string lexer and parser into this one, // by having a single lexer/parser instead of two different ones we can avoid // complex regular expressions like the one above, which is actually trying to // do some parsing in the lexer. alloc_sized_string(s, strlen(yytext)); strlcpy(s->c_string, yytext, s->length + 1); yylval->sized_string = s; return _HEX_STRING_; } YY_BREAK case 81: /* rule 81 can match eol */ YY_RULE_SETUP #line 746 "lexer.l" /* skip whitespace */ YY_BREAK case 82: YY_RULE_SETUP #line 748 "lexer.l" { if (yytext[0] >= 32 && yytext[0] < 127) { return yytext[0]; } else { syntax_error("non-ascii character"); } } YY_BREAK case 83: YY_RULE_SETUP #line 760 "lexer.l" ECHO; YY_BREAK #line 2282 "lexer.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc( (void *) b->yy_ch_buf, (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); /* "- 2" to take care of EOB's */ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { yy_state_type yy_current_state; char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 299 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ char *yy_cp = yyg->yy_c_buf_p; YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 299 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; yy_is_jam = (yy_current_state == 298); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; if ( c == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b , yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param _line_number line number * @param yyscanner The scanner object. */ void yyset_lineno (int _line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column number * @param yyscanner The scanner object. */ void yyset_column (int _column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_column called with no buffer" ); yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ void yyset_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = _in_str ; } void yyset_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = _out_str ; } int yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } /* User-visible API */ /* yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* yylex_init_extra has the same functionality as yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to yyalloc in * the yyextra field. */ int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = NULL; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ yyfree(yyg->yy_buffer_stack , yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ yyfree( yyg->yy_start_stack , yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(size); } void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 760 "lexer.l" void yywarning( yyscan_t yyscanner, const char *message_fmt, ...) { YR_COMPILER* compiler = yyget_extra(yyscanner); char* file_name; char message[512]; va_list message_args; if (compiler->callback == NULL) return; va_start(message_args, message_fmt); if (compiler->file_name_stack_ptr > 0) file_name = compiler->file_name_stack[compiler->file_name_stack_ptr - 1]; else file_name = NULL; vsnprintf(message, sizeof(message), message_fmt, message_args); YR_RULE* current_rule = NULL; if (compiler->current_rule_idx != UINT32_MAX) current_rule = yr_arena_get_ptr( compiler->arena, YR_RULES_TABLE, compiler->current_rule_idx * sizeof(YR_RULE)); compiler->callback( YARA_ERROR_LEVEL_WARNING, file_name, compiler->current_line ? compiler->current_line : yyget_lineno(yyscanner), current_rule, message, compiler->user_data); va_end(message_args); } void yyfatal( yyscan_t yyscanner, const char *error_message) { YR_COMPILER* compiler = yyget_extra(yyscanner); yyerror(yyscanner, compiler, error_message); longjmp(compiler->error_recovery, 1); } void yyerror( yyscan_t yyscanner, YR_COMPILER* compiler, const char *error_message) { char message[512] = {'\0'}; char* file_name = NULL; /* if error_message != NULL the error comes from yyparse internal code else the error comes from my code and the error code is set in compiler->last_error */ compiler->errors++; if (compiler->current_line != 0) compiler->last_error_line = compiler->current_line; else compiler->last_error_line = yyget_lineno(yyscanner); compiler->current_line = 0; if (compiler->file_name_stack_ptr > 0) { file_name = compiler->file_name_stack[compiler->file_name_stack_ptr - 1]; } else { file_name = NULL; } YR_RULE* current_rule = NULL; if (compiler->current_rule_idx != UINT32_MAX) current_rule = yr_arena_get_ptr( compiler->arena, YR_RULES_TABLE, compiler->current_rule_idx * sizeof(YR_RULE)); if (error_message != NULL) { yr_compiler_set_error_extra_info(compiler, error_message); compiler->last_error = ERROR_SYNTAX_ERROR; if (compiler->callback != NULL) { compiler->callback( YARA_ERROR_LEVEL_ERROR, file_name, compiler->last_error_line, current_rule, error_message, compiler->user_data); } } else if (compiler->callback != NULL) { yr_compiler_get_error_message(compiler, message, sizeof(message)); compiler->callback( YARA_ERROR_LEVEL_ERROR, file_name, compiler->last_error_line, current_rule, message, compiler->user_data); } } int yr_lex_parse_rules_string( const char* rules_string, YR_COMPILER* compiler) { yyscan_t yyscanner; compiler->errors = 0; if (setjmp(compiler->error_recovery) != 0) return compiler->errors; yylex_init(&yyscanner); #if YYDEBUG yydebug = 1; #endif yyset_extra(compiler, yyscanner); yy_scan_string(rules_string, yyscanner); yyset_lineno(1, yyscanner); yyparse(yyscanner, compiler); yylex_destroy(yyscanner); return compiler->errors; } int yr_lex_parse_rules_file( FILE* rules_file, YR_COMPILER* compiler) { yyscan_t yyscanner; compiler->errors = 0; if (setjmp(compiler->error_recovery) != 0) return compiler->errors; yylex_init(&yyscanner); #if YYDEBUG yydebug = 1; #endif yyset_in(rules_file, yyscanner); yyset_extra(compiler, yyscanner); yyparse(yyscanner, compiler); yylex_destroy(yyscanner); return compiler->errors; } int yr_lex_parse_rules_fd( YR_FILE_DESCRIPTOR rules_fd, YR_COMPILER* compiler) { yyscan_t yyscanner; size_t file_size; void* buffer; #if defined(_WIN32) || defined(__CYGWIN__) DWORD bytes_read; #endif compiler->errors = 0; if (setjmp(compiler->error_recovery) != 0) return compiler->errors; #if defined(_WIN32) || defined(__CYGWIN__) file_size = (size_t) GetFileSize(rules_fd, NULL); #else struct stat fs; if (fstat(rules_fd, &fs) != 0) { compiler->errors = 1; compiler->last_error = ERROR_COULD_NOT_READ_FILE; return compiler->errors; } file_size = (size_t) fs.st_size; #endif buffer = yr_malloc(file_size); if (buffer == NULL) { compiler->errors = 1; compiler->last_error = ERROR_INSUFFICIENT_MEMORY; return compiler->errors; } #if defined(_WIN32) || defined(__CYGWIN__) if (!ReadFile(rules_fd, buffer, file_size, &bytes_read, NULL)) #else if (read(rules_fd, buffer, file_size) != file_size) #endif { yr_free(buffer); compiler->errors = 1; compiler->last_error = ERROR_COULD_NOT_READ_FILE; return compiler->errors; } yylex_init(&yyscanner); #if YYDEBUG yydebug = 1; #endif yyset_extra(compiler, yyscanner); yy_scan_bytes((const char*) buffer, (int) file_size, yyscanner); yyset_lineno(1, yyscanner); yyparse(yyscanner, compiler); yylex_destroy(yyscanner); yr_free(buffer); return compiler->errors; } yara-4.1.3/libyara/lexer.l000066400000000000000000000533121413423160300153730ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Lexical analyzer for YARA */ %{ /* Disable warnings for unused functions in this file. As we redefine YY_FATAL_ERROR macro to use our own function yara_yyfatal, the yy_fatal_error function generated by Flex is not actually used, causing a compiler warning. Flex doesn't offer any options to remove the yy_fatal_error function. When they include something like %option noyy_fatal_error as they do with noyywrap then we can remove this pragma. */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #include #include #include #if defined(_WIN32) || defined(__CYGWIN__) #include #else #include #include #endif #if defined(_WIN32) #define strtoll _strtoi64 #endif #include #include #include #include #include #include #include #include "grammar.h" #define error(error_code) \ { \ compiler->last_error = error_code; \ yyerror(yyscanner, compiler, NULL); \ yyterminate(); \ } #define syntax_error(error_msg) \ { \ yr_compiler_set_error_extra_info(compiler, error_msg); \ error(ERROR_SYNTAX_ERROR); \ } #define lex_check_space_ok(data, current_size, max_length) \ if (strlen(data) + current_size >= max_length - 1) \ { \ yyerror(yyscanner, compiler, "out of space in lex_buf"); \ yyterminate(); \ } #define yytext_to_buffer \ { \ char *yptr = yytext; \ lex_check_space_ok(yptr, yyextra->lex_buf_len, YR_LEX_BUF_SIZE); \ while(*yptr) \ { \ *yyextra->lex_buf_ptr++ = *yptr++; \ yyextra->lex_buf_len++; \ } \ } #define alloc_sized_string(str, str_len) \ SIZED_STRING* str = (SIZED_STRING*) yr_malloc( \ str_len + sizeof(SIZED_STRING)); \ if (str == NULL) \ { \ yyerror(yyscanner, compiler, "not enough memory"); \ yyterminate(); \ } \ else \ { \ str->length = (uint32_t) (str_len); \ str->flags = 0; \ } \ #ifdef _WIN32 #define snprintf _snprintf #endif static bool is_absolute_path( char* path) { if (path == NULL) return false; #if defined(_WIN32) || defined(__CYGWIN__) return strlen(path) > 2 && path[1] == ':' && (path[2] == '/' || path[2] == '\\'); #else return strlen(path) > 0 && path[0] == '/'; #endif } %} %option reentrant bison-bridge %option noyywrap %option nounistd %option noinput %option nounput %option never-interactive %option yylineno %option prefix="yara_yy" %option outfile="lex.yy.c" %option verbose %option warn %x str %x regexp %x include %x comment digit [0-9] letter [a-zA-Z] hexdigit [a-fA-F0-9] octdigit [0-7] %% ".." { return _DOT_DOT_; } "<" { return _LT_; } ">" { return _GT_; } "<=" { return _LE_; } ">=" { return _GE_; } "==" { return _EQ_; } "!=" { return _NEQ_; } "<<" { return _SHIFT_LEFT_; } ">>" { return _SHIFT_RIGHT_; } "private" { return _PRIVATE_; } "global" { return _GLOBAL_; } "rule" { return _RULE_; } "meta" { return _META_; } "strings" { return _STRINGS_; } "ascii" { return _ASCII_; } "wide" { return _WIDE_; } "xor" { return _XOR_; } "base64" { return _BASE64_; } "base64wide" { return _BASE64_WIDE_; } "fullword" { return _FULLWORD_; } "nocase" { return _NOCASE_; } "condition" { return _CONDITION_; } "true" { return _TRUE_; } "false" { return _FALSE_; } "not" { return _NOT_; } "and" { return _AND_; } "or" { return _OR_; } "at" { return _AT_; } "in" { return _IN_; } "of" { return _OF_; } "them" { return _THEM_; } "for" { return _FOR_; } "all" { return _ALL_; } "any" { return _ANY_; } "entrypoint" { return _ENTRYPOINT_; } "filesize" { return _FILESIZE_; } "matches" { return _MATCHES_; } "contains" { return _CONTAINS_; } "startswith" { return _STARTSWITH_; } "endswith" { return _ENDSWITH_; } "icontains" { return _ICONTAINS_; } "istartswith" { return _ISTARTSWITH_; } "iendswith" { return _IENDSWITH_; } "import" { return _IMPORT_; } "/*" { BEGIN(comment); } "*/" { BEGIN(INITIAL); } (.|\n) { /* skip comments */ } "//"[^\n]* { /* skip single-line comments */ } include[ \t]+\" { yyextra->lex_buf_ptr = yyextra->lex_buf; yyextra->lex_buf_len = 0; BEGIN(include); } [^\"]+ { yytext_to_buffer; } \" { if (compiler->include_callback != NULL) { #ifdef _MSC_VER char* b = NULL; #endif char* s = NULL; char* f; char buffer[1024]; const char* included_rules; char* current_file_name; char* include_path; *yyextra->lex_buf_ptr = '\0'; // null-terminate included file path current_file_name = yr_compiler_get_current_file_name(compiler); if (current_file_name == NULL || compiler->include_callback != _yr_compiler_default_include_callback || is_absolute_path(yyextra->lex_buf)) { include_path = yyextra->lex_buf; } else { strlcpy(buffer, current_file_name, sizeof(buffer)); s = strrchr(buffer, '/'); #ifdef _MSC_VER b = strrchr(buffer, '\\'); // in Windows both path delimiters are accepted #endif #ifdef _MSC_VER if (s != NULL || b != NULL) #else if (s != NULL) #endif { #ifdef _MSC_VER f = (b > s) ? (b + 1) : (s + 1); #else f = s + 1; #endif strlcpy(f, yyextra->lex_buf, sizeof(buffer) - (f - buffer)); include_path = buffer; } else { include_path = yyextra->lex_buf; } } YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE)); included_rules = compiler->include_callback( include_path, current_file_name, ns->name, compiler->incl_clbk_user_data); if (included_rules != NULL) { int error_code = _yr_compiler_push_file_name(compiler, include_path); if (error_code != ERROR_SUCCESS) { if (error_code == ERROR_INCLUDES_CIRCULAR_REFERENCE) { yyerror(yyscanner, compiler, "includes circular reference"); } else if (error_code == ERROR_INCLUDE_DEPTH_EXCEEDED) { yyerror(yyscanner, compiler, "includes depth exceeded"); } if (compiler->include_free != NULL) { compiler->include_free(included_rules, compiler->incl_clbk_user_data); } yyterminate(); } // Workaround for flex issue: https://github.com/westes/flex/issues/58 yypush_buffer_state(YY_CURRENT_BUFFER, yyscanner); yy_scan_string(included_rules, yyscanner); yyset_lineno(1, yyscanner); if (compiler->include_free != NULL) { compiler->include_free(included_rules, compiler->incl_clbk_user_data); } } else { char* err_msg_fmt; char err_msg[512]; if (compiler->include_callback == _yr_compiler_default_include_callback) { err_msg_fmt = "can't open include file: %s"; } else { err_msg_fmt = "callback failed to provide include resource: %s"; } snprintf( err_msg, sizeof(err_msg), err_msg_fmt, yyextra->lex_buf); yyerror(yyscanner, compiler, err_msg); } } else // not allowing includes { yyerror(yyscanner, compiler, "includes are disabled"); } BEGIN(INITIAL); } <> { yypop_buffer_state(yyscanner); if (!YY_CURRENT_BUFFER) yyterminate(); return _END_OF_INCLUDED_FILE_; } $({letter}|{digit}|_)*"*" { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) error(ERROR_INSUFFICIENT_MEMORY); return _STRING_IDENTIFIER_WITH_WILDCARD_; } $({letter}|{digit}|_)* { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) error(ERROR_INSUFFICIENT_MEMORY); return _STRING_IDENTIFIER_; } #({letter}|{digit}|_)* { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) { error(ERROR_INSUFFICIENT_MEMORY); } else { yylval->c_string[0] = '$'; /* replace # by $*/ } return _STRING_COUNT_; } @({letter}|{digit}|_)* { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) { error(ERROR_INSUFFICIENT_MEMORY); } else { yylval->c_string[0] = '$'; /* replace @ by $*/ } return _STRING_OFFSET_; } !({letter}|{digit}|_)* { yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) { error(ERROR_INSUFFICIENT_MEMORY); } else { yylval->c_string[0] = '$'; /* replace ! by $*/ } return _STRING_LENGTH_; } u?int(8|16|32)(be)? { char* text = yytext; if (*text == 'u') { yylval->integer = 3; text++; } else { yylval->integer = 0; } if (strstr(text, "int8") == text) { yylval->integer += 0; text += 4; } else if (strstr(text, "int16") == text) { yylval->integer += 1; text += 5; } else if (strstr(text, "int32") == text) { yylval->integer += 2; text += 5; } if (strcmp(text, "be") == 0) { yylval->integer += 6; } return _INTEGER_FUNCTION_; } ({letter}|_)({letter}|{digit}|_)* { if (strlen(yytext) > 128) syntax_error("identifier too long"); yylval->c_string = yr_strdup(yytext); if (yylval->c_string == NULL) error(ERROR_INSUFFICIENT_MEMORY); return _IDENTIFIER_; } {digit}+(MB|KB){0,1} { char *endptr; errno = 0; yylval->integer = strtoll(yytext, &endptr, 10); if (yylval->integer == LLONG_MAX && errno == ERANGE) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } else if (strstr(yytext, "KB") != NULL) { if (yylval->integer > LLONG_MAX / 1024) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } else { yylval->integer *= 1024; } } else if (strstr(yytext, "MB") != NULL) { if (yylval->integer > LLONG_MAX / 1048576) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } else { yylval->integer *= 1048576; } } return _NUMBER_; } {digit}+"."{digit}+ { yylval->double_ = atof(yytext); return _DOUBLE_; } 0x{hexdigit}+ { char *endptr; errno = 0; yylval->integer = strtoll(yytext, &endptr, 16); if (yylval->integer == LLONG_MAX && errno == ERANGE) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } return _NUMBER_; } 0o{octdigit}+ { char *endptr; errno = 0; yylval->integer = strtoll(yytext + 2, &endptr, 8); if (yylval->integer == LLONG_MAX && errno == ERANGE) { yr_compiler_set_error_extra_info(compiler, yytext); error(ERROR_INTEGER_OVERFLOW); } return _NUMBER_; } \" { /* saw closing quote - all done */ alloc_sized_string(s, yyextra->lex_buf_len); *yyextra->lex_buf_ptr = '\0'; memcpy(s->c_string, yyextra->lex_buf, yyextra->lex_buf_len + 1); yylval->sized_string = s; BEGIN(INITIAL); return _TEXT_STRING_; } \\t { lex_check_space_ok("\t", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\t'; yyextra->lex_buf_len++; } \\r { lex_check_space_ok("\r", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\r'; yyextra->lex_buf_len++; } \\n { lex_check_space_ok("\n", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\n'; yyextra->lex_buf_len++; } \\\" { lex_check_space_ok("\"", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\"'; yyextra->lex_buf_len++; } \\\\ { lex_check_space_ok("\\", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '\\'; yyextra->lex_buf_len++; } \\x{hexdigit}{2} { int result; sscanf(yytext + 2, "%x", &result); lex_check_space_ok("X", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = result; yyextra->lex_buf_len++; } [^\\\n\"]+ { yytext_to_buffer; } \n { syntax_error("unterminated string"); } \\(.|\n) { syntax_error("illegal escape sequence"); } \/i?s? { if (yyextra->lex_buf_len > 0) { alloc_sized_string(s, yyextra->lex_buf_len); if (yytext[1] == 'i') s->flags |= SIZED_STRING_FLAGS_NO_CASE; if (yytext[1] == 's' || yytext[2] == 's') s->flags |= SIZED_STRING_FLAGS_DOT_ALL; *yyextra->lex_buf_ptr = '\0'; strlcpy(s->c_string, yyextra->lex_buf, s->length + 1); yylval->sized_string = s; } else { syntax_error("empty regular expression"); } BEGIN(INITIAL); return _REGEXP_; } \\\/ { lex_check_space_ok("/", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); *yyextra->lex_buf_ptr++ = '/'; yyextra->lex_buf_len++ ; } \\. { lex_check_space_ok("\\.", yyextra->lex_buf_len, YR_LEX_BUF_SIZE); if (yytext[1] == 0) syntax_error("malformed regular expression"); *yyextra->lex_buf_ptr++ = yytext[0]; *yyextra->lex_buf_ptr++ = yytext[1]; yyextra->lex_buf_len += 2; } [^/\n\\]+ { yytext_to_buffer; } \n { syntax_error("unterminated regular expression"); } \" { yylval->sized_string = NULL; yyextra->lex_buf_ptr = yyextra->lex_buf; yyextra->lex_buf_len = 0; BEGIN(str); } "/" { yylval->sized_string = NULL; yyextra->lex_buf_ptr = yyextra->lex_buf; yyextra->lex_buf_len = 0; BEGIN(regexp); } \{(({hexdigit}|[ \-|\?\[\]\(\)\n\r\t]|\/\*(\/|\**[^*/])*\*+\/)+|\/\/.*)+\} { // Match hex-digits with whitespace or comments. The latter are stripped // out by hex_lexer.l // TODO(vmalvarez): Integrate the hex string lexer and parser into this one, // by having a single lexer/parser instead of two different ones we can avoid // complex regular expressions like the one above, which is actually trying to // do some parsing in the lexer. alloc_sized_string(s, strlen(yytext)); strlcpy(s->c_string, yytext, s->length + 1); yylval->sized_string = s; return _HEX_STRING_; } [ \t\r\n] /* skip whitespace */ . { if (yytext[0] >= 32 && yytext[0] < 127) { return yytext[0]; } else { syntax_error("non-ascii character"); } } %% void yywarning( yyscan_t yyscanner, const char *message_fmt, ...) { YR_COMPILER* compiler = yyget_extra(yyscanner); char* file_name; char message[512]; va_list message_args; if (compiler->callback == NULL) return; va_start(message_args, message_fmt); if (compiler->file_name_stack_ptr > 0) file_name = compiler->file_name_stack[compiler->file_name_stack_ptr - 1]; else file_name = NULL; vsnprintf(message, sizeof(message), message_fmt, message_args); YR_RULE* current_rule = NULL; if (compiler->current_rule_idx != UINT32_MAX) current_rule = yr_arena_get_ptr( compiler->arena, YR_RULES_TABLE, compiler->current_rule_idx * sizeof(YR_RULE)); compiler->callback( YARA_ERROR_LEVEL_WARNING, file_name, compiler->current_line ? compiler->current_line : yyget_lineno(yyscanner), current_rule, message, compiler->user_data); va_end(message_args); } void yyfatal( yyscan_t yyscanner, const char *error_message) { YR_COMPILER* compiler = yyget_extra(yyscanner); yyerror(yyscanner, compiler, error_message); longjmp(compiler->error_recovery, 1); } void yyerror( yyscan_t yyscanner, YR_COMPILER* compiler, const char *error_message) { char message[512] = {'\0'}; char* file_name = NULL; /* if error_message != NULL the error comes from yyparse internal code else the error comes from my code and the error code is set in compiler->last_error */ compiler->errors++; if (compiler->current_line != 0) compiler->last_error_line = compiler->current_line; else compiler->last_error_line = yyget_lineno(yyscanner); compiler->current_line = 0; if (compiler->file_name_stack_ptr > 0) { file_name = compiler->file_name_stack[compiler->file_name_stack_ptr - 1]; } else { file_name = NULL; } YR_RULE* current_rule = NULL; if (compiler->current_rule_idx != UINT32_MAX) current_rule = yr_arena_get_ptr( compiler->arena, YR_RULES_TABLE, compiler->current_rule_idx * sizeof(YR_RULE)); if (error_message != NULL) { yr_compiler_set_error_extra_info(compiler, error_message); compiler->last_error = ERROR_SYNTAX_ERROR; if (compiler->callback != NULL) { compiler->callback( YARA_ERROR_LEVEL_ERROR, file_name, compiler->last_error_line, current_rule, error_message, compiler->user_data); } } else if (compiler->callback != NULL) { yr_compiler_get_error_message(compiler, message, sizeof(message)); compiler->callback( YARA_ERROR_LEVEL_ERROR, file_name, compiler->last_error_line, current_rule, message, compiler->user_data); } } int yr_lex_parse_rules_string( const char* rules_string, YR_COMPILER* compiler) { yyscan_t yyscanner; compiler->errors = 0; if (setjmp(compiler->error_recovery) != 0) return compiler->errors; yylex_init(&yyscanner); #if YYDEBUG yydebug = 1; #endif yyset_extra(compiler, yyscanner); yy_scan_string(rules_string, yyscanner); yyset_lineno(1, yyscanner); yyparse(yyscanner, compiler); yylex_destroy(yyscanner); return compiler->errors; } int yr_lex_parse_rules_file( FILE* rules_file, YR_COMPILER* compiler) { yyscan_t yyscanner; compiler->errors = 0; if (setjmp(compiler->error_recovery) != 0) return compiler->errors; yylex_init(&yyscanner); #if YYDEBUG yydebug = 1; #endif yyset_in(rules_file, yyscanner); yyset_extra(compiler, yyscanner); yyparse(yyscanner, compiler); yylex_destroy(yyscanner); return compiler->errors; } int yr_lex_parse_rules_fd( YR_FILE_DESCRIPTOR rules_fd, YR_COMPILER* compiler) { yyscan_t yyscanner; size_t file_size; void* buffer; #if defined(_WIN32) || defined(__CYGWIN__) DWORD bytes_read; #endif compiler->errors = 0; if (setjmp(compiler->error_recovery) != 0) return compiler->errors; #if defined(_WIN32) || defined(__CYGWIN__) file_size = (size_t) GetFileSize(rules_fd, NULL); #else struct stat fs; if (fstat(rules_fd, &fs) != 0) { compiler->errors = 1; compiler->last_error = ERROR_COULD_NOT_READ_FILE; return compiler->errors; } file_size = (size_t) fs.st_size; #endif buffer = yr_malloc(file_size); if (buffer == NULL) { compiler->errors = 1; compiler->last_error = ERROR_INSUFFICIENT_MEMORY; return compiler->errors; } #if defined(_WIN32) || defined(__CYGWIN__) if (!ReadFile(rules_fd, buffer, file_size, &bytes_read, NULL)) #else if (read(rules_fd, buffer, file_size) != file_size) #endif { yr_free(buffer); compiler->errors = 1; compiler->last_error = ERROR_COULD_NOT_READ_FILE; return compiler->errors; } yylex_init(&yyscanner); #if YYDEBUG yydebug = 1; #endif yyset_extra(compiler, yyscanner); yy_scan_bytes((const char*) buffer, (int) file_size, yyscanner); yyset_lineno(1, yyscanner); yyparse(yyscanner, compiler); yylex_destroy(yyscanner); yr_free(buffer); return compiler->errors; } yara-4.1.3/libyara/libyara.c000066400000000000000000000365271413423160300156770ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(JEMALLOC) #include #endif #include #include #include #include #include #include #include #include #include #include "crypto.h" #if defined(_WIN32) || defined(__CYGWIN__) #if !defined(_MSC_VER) || (defined(_MSC_VER) && (_MSC_VER < 1900)) #define snprintf _snprintf #endif #endif YR_THREAD_STORAGE_KEY yr_yyfatal_trampoline_tls; YR_THREAD_STORAGE_KEY yr_trycatch_trampoline_tls; static int init_count = 0; static struct yr_config_var { union { size_t sz; uint32_t ui32; uint64_t ui64; char *str; }; } yr_cfgs[YR_CONFIG_LAST]; // Global variables. See globals.h for their descriptions. uint8_t yr_lowercase[256]; uint8_t yr_altercase[256]; #if 0 == YR_DEBUG_VERBOSITY #else uint64_t yr_debug_verbosity = YR_DEBUG_VERBOSITY; YR_TLS int yr_debug_indent = 0; YR_TLS int yr_debug_stopwatch_unstarted = 1; YR_TLS YR_STOPWATCH yr_debug_stopwatch; const char yr_debug_spaces[] = " " /* 16 spaces * 1 */ " " /* 16 spaces * 2 */ " " /* 16 spaces * 3 */ " " /* 16 spaces * 4 */ " " /* 16 spaces * 5 */ " " /* 16 spaces * 6 */ " " /* 16 spaces * 7 */ " " /* 16 spaces * 8 */; size_t yr_debug_spaces_len = sizeof(yr_debug_spaces); double yr_debug_get_elapsed_seconds(void) { if (yr_debug_stopwatch_unstarted) { yr_debug_stopwatch_unstarted = 0; yr_stopwatch_start(&yr_debug_stopwatch); } uint64_t elapsed_ns = yr_stopwatch_elapsed_ns(&yr_debug_stopwatch); double seconds = (double) elapsed_ns / 1000000000; return seconds; } char *yr_debug_callback_message_as_string(int message) { char *s = "CALLBACK_MSG_?"; switch (message) { // clang-format off case CALLBACK_MSG_RULE_MATCHING : s = "CALLBACK_MSG_RULE_MATCHING" ; break; case CALLBACK_MSG_RULE_NOT_MATCHING: s = "CALLBACK_MSG_RULE_NOT_MATCHING"; break; case CALLBACK_MSG_SCAN_FINISHED : s = "CALLBACK_MSG_SCAN_FINISHED" ; break; case CALLBACK_MSG_IMPORT_MODULE : s = "CALLBACK_MSG_IMPORT_MODULE" ; break; case CALLBACK_MSG_MODULE_IMPORTED : s = "CALLBACK_MSG_MODULE_IMPORTED" ; break; } // clang-format on return s; } char *yr_debug_error_as_string(int error) { char *s = "ERROR_?"; switch (error) { // clang-format off case ERROR_SUCCESS : s = "ERROR_SUCCESS 0" ; break; case ERROR_INSUFFICIENT_MEMORY : s = "ERROR_INSUFFICIENT_MEMORY" ; break; case ERROR_COULD_NOT_ATTACH_TO_PROCESS : s = "ERROR_COULD_NOT_ATTACH_TO_PROCESS" ; break; case ERROR_COULD_NOT_OPEN_FILE : s = "ERROR_COULD_NOT_OPEN_FILE" ; break; case ERROR_COULD_NOT_MAP_FILE : s = "ERROR_COULD_NOT_MAP_FILE" ; break; case ERROR_INVALID_FILE : s = "ERROR_INVALID_FILE" ; break; case ERROR_CORRUPT_FILE : s = "ERROR_CORRUPT_FILE" ; break; case ERROR_UNSUPPORTED_FILE_VERSION : s = "ERROR_UNSUPPORTED_FILE_VERSION" ; break; case ERROR_INVALID_REGULAR_EXPRESSION : s = "ERROR_INVALID_REGULAR_EXPRESSION" ; break; case ERROR_INVALID_HEX_STRING : s = "ERROR_INVALID_HEX_STRING" ; break; case ERROR_SYNTAX_ERROR : s = "ERROR_SYNTAX_ERROR" ; break; case ERROR_LOOP_NESTING_LIMIT_EXCEEDED : s = "ERROR_LOOP_NESTING_LIMIT_EXCEEDED" ; break; case ERROR_DUPLICATED_LOOP_IDENTIFIER : s = "ERROR_DUPLICATED_LOOP_IDENTIFIER" ; break; case ERROR_DUPLICATED_IDENTIFIER : s = "ERROR_DUPLICATED_IDENTIFIER" ; break; case ERROR_DUPLICATED_TAG_IDENTIFIER : s = "ERROR_DUPLICATED_TAG_IDENTIFIER" ; break; case ERROR_DUPLICATED_META_IDENTIFIER : s = "ERROR_DUPLICATED_META_IDENTIFIER" ; break; case ERROR_DUPLICATED_STRING_IDENTIFIER : s = "ERROR_DUPLICATED_STRING_IDENTIFIER" ; break; case ERROR_UNREFERENCED_STRING : s = "ERROR_UNREFERENCED_STRING" ; break; case ERROR_UNDEFINED_STRING : s = "ERROR_UNDEFINED_STRING" ; break; case ERROR_UNDEFINED_IDENTIFIER : s = "ERROR_UNDEFINED_IDENTIFIER" ; break; case ERROR_MISPLACED_ANONYMOUS_STRING : s = "ERROR_MISPLACED_ANONYMOUS_STRING" ; break; case ERROR_INCLUDES_CIRCULAR_REFERENCE : s = "ERROR_INCLUDES_CIRCULAR_REFERENCE" ; break; case ERROR_INCLUDE_DEPTH_EXCEEDED : s = "ERROR_INCLUDE_DEPTH_EXCEEDED" ; break; case ERROR_WRONG_TYPE : s = "ERROR_WRONG_TYPE" ; break; case ERROR_EXEC_STACK_OVERFLOW : s = "ERROR_EXEC_STACK_OVERFLOW" ; break; case ERROR_SCAN_TIMEOUT : s = "ERROR_SCAN_TIMEOUT" ; break; case ERROR_TOO_MANY_SCAN_THREADS : s = "ERROR_TOO_MANY_SCAN_THREADS" ; break; case ERROR_CALLBACK_ERROR : s = "ERROR_CALLBACK_ERROR" ; break; case ERROR_INVALID_ARGUMENT : s = "ERROR_INVALID_ARGUMENT" ; break; case ERROR_TOO_MANY_MATCHES : s = "ERROR_TOO_MANY_MATCHES" ; break; case ERROR_INTERNAL_FATAL_ERROR : s = "ERROR_INTERNAL_FATAL_ERROR" ; break; case ERROR_NESTED_FOR_OF_LOOP : s = "ERROR_NESTED_FOR_OF_LOOP" ; break; case ERROR_INVALID_FIELD_NAME : s = "ERROR_INVALID_FIELD_NAME" ; break; case ERROR_UNKNOWN_MODULE : s = "ERROR_UNKNOWN_MODULE" ; break; case ERROR_NOT_A_STRUCTURE : s = "ERROR_NOT_A_STRUCTURE" ; break; case ERROR_NOT_INDEXABLE : s = "ERROR_NOT_INDEXABLE" ; break; case ERROR_NOT_A_FUNCTION : s = "ERROR_NOT_A_FUNCTION" ; break; case ERROR_INVALID_FORMAT : s = "ERROR_INVALID_FORMAT" ; break; case ERROR_TOO_MANY_ARGUMENTS : s = "ERROR_TOO_MANY_ARGUMENTS" ; break; case ERROR_WRONG_ARGUMENTS : s = "ERROR_WRONG_ARGUMENTS" ; break; case ERROR_WRONG_RETURN_TYPE : s = "ERROR_WRONG_RETURN_TYPE" ; break; case ERROR_DUPLICATED_STRUCTURE_MEMBER : s = "ERROR_DUPLICATED_STRUCTURE_MEMBER" ; break; case ERROR_EMPTY_STRING : s = "ERROR_EMPTY_STRING" ; break; case ERROR_DIVISION_BY_ZERO : s = "ERROR_DIVISION_BY_ZERO" ; break; case ERROR_REGULAR_EXPRESSION_TOO_LARGE : s = "ERROR_REGULAR_EXPRESSION_TOO_LARGE" ; break; case ERROR_TOO_MANY_RE_FIBERS : s = "ERROR_TOO_MANY_RE_FIBERS" ; break; case ERROR_COULD_NOT_READ_PROCESS_MEMORY : s = "ERROR_COULD_NOT_READ_PROCESS_MEMORY" ; break; case ERROR_INVALID_EXTERNAL_VARIABLE_TYPE: s = "ERROR_INVALID_EXTERNAL_VARIABLE_TYPE"; break; case ERROR_REGULAR_EXPRESSION_TOO_COMPLEX: s = "ERROR_REGULAR_EXPRESSION_TOO_COMPLEX"; break; case ERROR_INVALID_MODULE_NAME : s = "ERROR_INVALID_MODULE_NAME" ; break; case ERROR_TOO_MANY_STRINGS : s = "ERROR_TOO_MANY_STRINGS" ; break; case ERROR_INTEGER_OVERFLOW : s = "ERROR_INTEGER_OVERFLOW" ; break; case ERROR_CALLBACK_REQUIRED : s = "ERROR_CALLBACK_REQUIRED" ; break; case ERROR_INVALID_OPERAND : s = "ERROR_INVALID_OPERAND" ; break; case ERROR_COULD_NOT_READ_FILE : s = "ERROR_COULD_NOT_READ_FILE" ; break; case ERROR_DUPLICATED_EXTERNAL_VARIABLE : s = "ERROR_DUPLICATED_EXTERNAL_VARIABLE" ; break; case ERROR_INVALID_MODULE_DATA : s = "ERROR_INVALID_MODULE_DATA" ; break; case ERROR_WRITING_FILE : s = "ERROR_WRITING_FILE" ; break; case ERROR_INVALID_MODIFIER : s = "ERROR_INVALID_MODIFIER" ; break; case ERROR_DUPLICATED_MODIFIER : s = "ERROR_DUPLICATED_MODIFIER" ; break; case ERROR_BLOCK_NOT_READY : s = "ERROR_BLOCK_NOT_READY" ; break; } // clang-format on return s; } #endif #if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER < 0x10100000L // The OpenSSL library before version 1.1 requires some locks in order // to be thread-safe. These locks are initialized in yr_initialize // function. static YR_MUTEX *openssl_locks; static void _thread_id(CRYPTO_THREADID *id) { CRYPTO_THREADID_set_numeric(id, (unsigned long) yr_current_thread_id()); } static void _locking_function(int mode, int n, const char *file, int line) { if (mode & CRYPTO_LOCK) yr_mutex_lock(&openssl_locks[n]); else yr_mutex_unlock(&openssl_locks[n]); } #endif #if defined(HAVE_WINCRYPT_H) HCRYPTPROV yr_cryptprov; #endif //////////////////////////////////////////////////////////////////////////////// // Should be called by main thread before using any other // function from libyara. // YR_API int yr_initialize(void) { YR_DEBUG_FPRINTF(2, stderr, "+ %s() {\n", __FUNCTION__); uint32_t def_stack_size = DEFAULT_STACK_SIZE; uint32_t def_max_strings_per_rule = DEFAULT_MAX_STRINGS_PER_RULE; uint32_t def_max_match_data = DEFAULT_MAX_MATCH_DATA; init_count++; if (init_count > 1) return ERROR_SUCCESS; // Initialize random number generator, as it is used for generating object // canaries. srand((unsigned) time(NULL)); for (int i = 0; i < 256; i++) { if (i >= 'a' && i <= 'z') yr_altercase[i] = i - 32; else if (i >= 'A' && i <= 'Z') yr_altercase[i] = i + 32; else yr_altercase[i] = i; yr_lowercase[i] = tolower(i); } FAIL_ON_ERROR(yr_heap_alloc()); FAIL_ON_ERROR(yr_thread_storage_create(&yr_yyfatal_trampoline_tls)); FAIL_ON_ERROR(yr_thread_storage_create(&yr_trycatch_trampoline_tls)); #if defined HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER < 0x10100000L openssl_locks = (YR_MUTEX *) OPENSSL_malloc( CRYPTO_num_locks() * sizeof(YR_MUTEX)); for (int i = 0; i < CRYPTO_num_locks(); i++) yr_mutex_create(&openssl_locks[i]); CRYPTO_THREADID_set_callback(_thread_id); CRYPTO_set_locking_callback(_locking_function); #elif defined(HAVE_WINCRYPT_H) if (!CryptAcquireContext( &yr_cryptprov, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) { return ERROR_INTERNAL_FATAL_ERROR; } #elif defined(HAVE_COMMON_CRYPTO) ... #endif FAIL_ON_ERROR(yr_modules_initialize()); // Initialize default configuration options FAIL_ON_ERROR(yr_set_configuration(YR_CONFIG_STACK_SIZE, &def_stack_size)); FAIL_ON_ERROR(yr_set_configuration( YR_CONFIG_MAX_STRINGS_PER_RULE, &def_max_strings_per_rule)); FAIL_ON_ERROR( yr_set_configuration(YR_CONFIG_MAX_MATCH_DATA, &def_max_match_data)); YR_DEBUG_FPRINTF(2, stderr, "} // %s()\n", __FUNCTION__); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Should be called by main thread before exiting. // YR_API int yr_finalize(void) { YR_DEBUG_FPRINTF(2, stderr, "+ %s() {\n", __FUNCTION__); #if defined HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER < 0x10100000L int i; #endif // yr_finalize shouldn't be called without calling yr_initialize first if (init_count == 0) return ERROR_INTERNAL_FATAL_ERROR; init_count--; if (init_count > 0) return ERROR_SUCCESS; #if defined HAVE_LIBCRYPTO && OPENSSL_VERSION_NUMBER < 0x10100000L for (i = 0; i < CRYPTO_num_locks(); i++) yr_mutex_destroy(&openssl_locks[i]); OPENSSL_free(openssl_locks); CRYPTO_THREADID_set_callback(NULL); CRYPTO_set_locking_callback(NULL); #elif defined(HAVE_WINCRYPT_H) CryptReleaseContext(yr_cryptprov, 0); #endif FAIL_ON_ERROR(yr_thread_storage_destroy(&yr_yyfatal_trampoline_tls)); FAIL_ON_ERROR(yr_thread_storage_destroy(&yr_trycatch_trampoline_tls)); FAIL_ON_ERROR(yr_modules_finalize()); FAIL_ON_ERROR(yr_heap_free()); #if defined(JEMALLOC) malloc_stats_print(NULL, NULL, NULL); mallctl("prof.dump", NULL, NULL, NULL, 0); #endif YR_DEBUG_FPRINTF(2, stderr, "} // %s()\n", __FUNCTION__); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Sets a configuration option. // // This function receives a configuration name, as defined by the YR_CONFIG_NAME // enum, and a pointer to the value being set. The type of the value depends on // the configuration name. // // Args: // name: Any of the values defined by the YR_CONFIG_NAME enum. Possible values // are: // YR_CONFIG_STACK_SIZE data type: uint32_t // YR_CONFIG_MAX_STRINGS_PER_RULE data type: uint32_t // YR_CONFIG_MAX_MATCH_DATA data type: uint32_t // // src: Pointer to the value being set for the option. // // Returns: // ERROR_SUCCESS // ERROR_INTERNAL_FATAL_ERROR // YR_API int yr_set_configuration(YR_CONFIG_NAME name, void *src) { if (src == NULL) return ERROR_INTERNAL_FATAL_ERROR; switch (name) { // lump all the cases using same types together in one cascade case YR_CONFIG_STACK_SIZE: case YR_CONFIG_MAX_STRINGS_PER_RULE: case YR_CONFIG_MAX_MATCH_DATA: yr_cfgs[name].ui32 = *(uint32_t *) src; break; default: return ERROR_INTERNAL_FATAL_ERROR; } return ERROR_SUCCESS; } YR_API int yr_get_configuration(YR_CONFIG_NAME name, void *dest) { if (dest == NULL) return ERROR_INTERNAL_FATAL_ERROR; switch (name) { // lump all the cases using same types together in one cascade case YR_CONFIG_STACK_SIZE: case YR_CONFIG_MAX_STRINGS_PER_RULE: case YR_CONFIG_MAX_MATCH_DATA: *(uint32_t *) dest = yr_cfgs[name].ui32; break; default: return ERROR_INTERNAL_FATAL_ERROR; } return ERROR_SUCCESS; } yara-4.1.3/libyara/mem.c000066400000000000000000000065041413423160300150220ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #if defined(_WIN32) || defined(__CYGWIN__) #include #include static HANDLE hHeap; int yr_heap_alloc(void) { hHeap = HeapCreate(0, 0x8000, 0); if (hHeap == NULL) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_heap_free(void) { if (HeapDestroy(hHeap)) return ERROR_SUCCESS; else return ERROR_INTERNAL_FATAL_ERROR; } void* yr_calloc(size_t count, size_t size) { return (void*) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, count * size); } void* yr_malloc(size_t size) { return (void*) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, size); } void* yr_realloc(void* ptr, size_t size) { if (ptr == NULL) return (void*) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, size); return (void*) HeapReAlloc(hHeap, HEAP_ZERO_MEMORY, ptr, size); } void yr_free(void* ptr) { HeapFree(hHeap, 0, ptr); } char* yr_strdup(const char* str) { size_t len = strlen(str); char* dup = (char*) yr_malloc(len + 1); if (dup == NULL) return NULL; memcpy(dup, str, len); dup[len] = '\0'; return (char*) dup; } char* yr_strndup(const char* str, size_t n) { size_t len = strnlen(str, n); char* dup = (char*) yr_malloc(len + 1); if (dup == NULL) return NULL; memcpy(dup, str, len); dup[len] = '\0'; return (char*) dup; } #else #include #include #include int yr_heap_alloc(void) { return ERROR_SUCCESS; } int yr_heap_free(void) { return ERROR_SUCCESS; } void* yr_calloc(size_t count, size_t size) { return calloc(count, size); } void* yr_malloc(size_t size) { return malloc(size); } void* yr_realloc(void* ptr, size_t size) { return realloc(ptr, size); } void yr_free(void* ptr) { free(ptr); } char* yr_strdup(const char* str) { return strdup(str); } char* yr_strndup(const char* str, size_t n) { return strndup(str, n); } #endif yara-4.1.3/libyara/modules.c000066400000000000000000000127251413423160300157160ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #define MODULE(name) \ int name##__declarations(YR_OBJECT* module); \ int name##__load( \ YR_SCAN_CONTEXT* context, \ YR_OBJECT* module, \ void* module_data, \ size_t module_data_size); \ int name##__unload(YR_OBJECT* main_structure); \ int name##__initialize(YR_MODULE* module); \ int name##__finalize(YR_MODULE* module); #include #undef MODULE #define MODULE(name) \ {#name, \ name##__declarations, \ name##__load, \ name##__unload, \ name##__initialize, \ name##__finalize}, YR_MODULE yr_modules_table[] = { #include }; #undef MODULE int yr_modules_initialize() { int i; for (i = 0; i < sizeof(yr_modules_table) / sizeof(YR_MODULE); i++) { int result = yr_modules_table[i].initialize(&yr_modules_table[i]); if (result != ERROR_SUCCESS) return result; } return ERROR_SUCCESS; } int yr_modules_finalize() { int i; for (i = 0; i < sizeof(yr_modules_table) / sizeof(YR_MODULE); i++) { int result = yr_modules_table[i].finalize(&yr_modules_table[i]); if (result != ERROR_SUCCESS) return result; } return ERROR_SUCCESS; } int yr_modules_do_declarations( const char* module_name, YR_OBJECT* main_structure) { int i; for (i = 0; i < sizeof(yr_modules_table) / sizeof(YR_MODULE); i++) { if (strcmp(yr_modules_table[i].name, module_name) == 0) return yr_modules_table[i].declarations(main_structure); } return ERROR_UNKNOWN_MODULE; } int yr_modules_load(const char* module_name, YR_SCAN_CONTEXT* context) { int i, result; YR_MODULE_IMPORT mi; YR_OBJECT* module_structure = (YR_OBJECT*) yr_hash_table_lookup( context->objects_table, module_name, NULL); // if module_structure != NULL, the module was already // loaded, return successfully without doing nothing. if (module_structure != NULL) return ERROR_SUCCESS; // not loaded yet FAIL_ON_ERROR(yr_object_create( OBJECT_TYPE_STRUCTURE, module_name, NULL, &module_structure)); // initialize canary for module's top-level structure, every other object // within the module inherits the same canary. yr_object_set_canary(module_structure, context->canary); mi.module_name = module_name; mi.module_data = NULL; mi.module_data_size = 0; result = context->callback( context, CALLBACK_MSG_IMPORT_MODULE, &mi, context->user_data); if (result == CALLBACK_ERROR) { yr_object_destroy(module_structure); return ERROR_CALLBACK_ERROR; } FAIL_ON_ERROR_WITH_CLEANUP( yr_modules_do_declarations(module_name, module_structure), yr_object_destroy(module_structure)); FAIL_ON_ERROR_WITH_CLEANUP( yr_hash_table_add( context->objects_table, module_name, NULL, module_structure), yr_object_destroy(module_structure)); for (i = 0; i < sizeof(yr_modules_table) / sizeof(YR_MODULE); i++) { if (strcmp(yr_modules_table[i].name, module_name) == 0) { result = yr_modules_table[i].load( context, module_structure, mi.module_data, mi.module_data_size); if (result != ERROR_SUCCESS) return result; } } result = context->callback( context, CALLBACK_MSG_MODULE_IMPORTED, module_structure, context->user_data); if (result == CALLBACK_ERROR) return ERROR_CALLBACK_ERROR; return ERROR_SUCCESS; } int yr_modules_unload_all(YR_SCAN_CONTEXT* context) { int i; for (i = 0; i < sizeof(yr_modules_table) / sizeof(YR_MODULE); i++) { YR_OBJECT* module_structure = (YR_OBJECT*) yr_hash_table_remove( context->objects_table, yr_modules_table[i].name, NULL); if (module_structure != NULL) { yr_modules_table[i].unload(module_structure); yr_object_destroy(module_structure); } } return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/000077500000000000000000000000001413423160300155435ustar00rootroot00000000000000yara-4.1.3/libyara/modules/cuckoo/000077500000000000000000000000001413423160300170265ustar00rootroot00000000000000yara-4.1.3/libyara/modules/cuckoo/cuckoo.c000066400000000000000000000253511413423160300204630ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #if defined(_WIN32) || defined(__CYGWIN__) #define strcasecmp _stricmp #endif #define MODULE_NAME cuckoo define_function(network_dns_lookup) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* network_obj = parent(); json_t* network_json = (json_t*) network_obj->data; json_t* value; uint64_t result = 0; size_t index; // Recent versions of Cuckoo generate domain resolution information with // this format: // // "domains": [ // { // "ip": "192.168.0.1", // "domain": "foo.bar.com" // } // ] // // But older versions with this other format: // // "dns": [ // { // "ip": "192.168.0.1", // "hostname": "foo.bar.com" // } // ] // // Additionally, the newer versions also have a "dns" field. So, let's try // to locate the "domains" field first, if not found fall back to the older // format. char* field_name = "domain"; char* hostname; char* ip; json_t* dns_info_json = json_object_get(network_json, "domains"); if (dns_info_json == NULL) { dns_info_json = json_object_get(network_json, "dns"); field_name = "hostname"; } json_array_foreach(dns_info_json, index, value) { if (json_unpack(value, "{s:s, s:s}", "ip", &ip, field_name, &hostname) == 0) { if (yr_re_match(context, regexp_argument(1), hostname) > 0) { result = 1; break; } } } return_integer(result); } #define METHOD_GET 0x01 #define METHOD_POST 0x02 uint64_t http_request( YR_SCAN_CONTEXT* context, YR_OBJECT* network_obj, RE* uri_regexp, int methods) { json_t* network_json = (json_t*) network_obj->data; json_t* http_json = json_object_get(network_json, "http"); json_t* value; uint64_t result = 0; size_t index; char* method; char* uri; json_array_foreach(http_json, index, value) { if (json_unpack(value, "{s:s, s:s}", "uri", &uri, "method", &method) == 0) { if (((methods & METHOD_GET && strcasecmp(method, "get") == 0) || (methods & METHOD_POST && strcasecmp(method, "post") == 0)) && yr_re_match(context, uri_regexp, uri) > 0) { result = 1; break; } } } return result; } define_function(network_http_request) { return_integer(http_request( scan_context(), parent(), regexp_argument(1), METHOD_GET | METHOD_POST)); } define_function(network_http_get) { return_integer( http_request(scan_context(), parent(), regexp_argument(1), METHOD_GET)); } define_function(network_http_post) { return_integer( http_request(scan_context(), parent(), regexp_argument(1), METHOD_POST)); } // checks the host array in network JSON // loops through array and checks value for argument define_function(network_host) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* network_obj = parent(); json_t* network_json = (json_t*) network_obj->data; json_t* value; uint64_t result = 0; size_t index; json_t* hosts_info_json = json_object_get(network_json, "hosts"); json_array_foreach(hosts_info_json, index, value) { if (yr_re_match(context, regexp_argument(1), json_string_value(value)) > 0) { result = 1; break; } } return_integer(result); } // loops through TCP array in network JSON // checks for dest IP regex and dest port integer match define_function(network_tcp) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* network_obj = parent(); json_t* network_json = (json_t*) network_obj->data; json_t* value; uint64_t result = 0; size_t index; int dport; char* dst; json_t* tcp_info_json = json_object_get(network_json, "tcp"); json_array_foreach(tcp_info_json, index, value) { if (json_unpack(value, "{s:i, s:s}", "dport", &dport, "dst", &dst) == 0) { if (yr_re_match(context, regexp_argument(1), dst) > 0) { if ((int64_t) dport == integer_argument(2)) { result = 1; break; } } } } return_integer(result); } // loops through UDP array in network JSON // checks for dest IP regex and dest port integer match define_function(network_udp) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* network_obj = parent(); json_t* network_json = (json_t*) network_obj->data; json_t* value; uint64_t result = 0; size_t index; int dport; char* dst; json_t* udp_info_json = json_object_get(network_json, "udp"); json_array_foreach(udp_info_json, index, value) { if (json_unpack(value, "{s:i, s:s}", "dport", &dport, "dst", &dst) == 0) { if (yr_re_match(context, regexp_argument(1), dst) > 0) { if ((int64_t) dport == integer_argument(2)) { result = 1; break; } } } } return_integer(result); } // loops through http array in network JSON // checks for regex match on user-agent define_function(network_http_user_agent) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* network_obj = parent(); json_t* network_json = (json_t*) network_obj->data; json_t* http_json = json_object_get(network_json, "http"); json_t* value; uint64_t result = 0; size_t index; char* user_agent; json_array_foreach(http_json, index, value) { if (json_unpack(value, "{s:s}", "user-agent", &user_agent) == 0) { if (yr_re_match(context, regexp_argument(1), user_agent) > 0) { result = 1; break; } } } return_integer(result); } define_function(registry_key_access) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* registry_obj = parent(); json_t* keys_json = (json_t*) registry_obj->data; json_t* value; uint64_t result = 0; size_t index; json_array_foreach(keys_json, index, value) { if (yr_re_match(context, regexp_argument(1), json_string_value(value)) > 0) { result = 1; break; } } return_integer(result); } define_function(filesystem_file_access) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* filesystem_obj = parent(); json_t* files_json = (json_t*) filesystem_obj->data; json_t* value; uint64_t result = 0; size_t index; json_array_foreach(files_json, index, value) { if (yr_re_match(context, regexp_argument(1), json_string_value(value)) > 0) { result = 1; break; } } return_integer(result); } define_function(sync_mutex) { YR_SCAN_CONTEXT* context = scan_context(); YR_OBJECT* sync_obj = parent(); json_t* mutexes_json = (json_t*) sync_obj->data; json_t* value; uint64_t result = 0; size_t index; json_array_foreach(mutexes_json, index, value) { if (yr_re_match(context, regexp_argument(1), json_string_value(value)) > 0) { result = 1; break; } } return_integer(result); } begin_declarations begin_struct("network") declare_function("dns_lookup", "r", "i", network_dns_lookup); declare_function("http_get", "r", "i", network_http_get); declare_function("http_post", "r", "i", network_http_post); declare_function("http_request", "r", "i", network_http_request); declare_function("http_user_agent", "r", "i", network_http_user_agent); declare_function("host", "r", "i", network_host); declare_function("tcp", "ri", "i", network_tcp); declare_function("udp", "ri", "i", network_udp); end_struct("network") begin_struct("registry") declare_function("key_access", "r", "i", registry_key_access); end_struct("registry") begin_struct("filesystem") declare_function("file_access", "r", "i", filesystem_file_access); end_struct("filesystem") begin_struct("sync") declare_function("mutex", "r", "i", sync_mutex) end_struct("sync") end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_OBJECT* network_obj; YR_OBJECT* registry_obj; YR_OBJECT* filesystem_obj; YR_OBJECT* sync_obj; json_error_t json_error; json_t* summary_json; json_t* json; if (module_data == NULL) return ERROR_SUCCESS; json = json_loadb( (const char*) module_data, module_data_size, #if JANSSON_VERSION_HEX >= 0x020600 JSON_ALLOW_NUL, #else 0, #endif &json_error); if (json == NULL) return ERROR_INVALID_MODULE_DATA; module_object->data = (void*) json; network_obj = get_object(module_object, "network"); registry_obj = get_object(module_object, "registry"); filesystem_obj = get_object(module_object, "filesystem"); sync_obj = get_object(module_object, "sync"); network_obj->data = (void*) json_object_get(json, "network"); json = json_object_get(json, "behavior"); summary_json = json_object_get(json, "summary"); registry_obj->data = (void*) json_object_get(summary_json, "keys"); filesystem_obj->data = (void*) json_object_get(summary_json, "files"); sync_obj->data = (void*) json_object_get(summary_json, "mutexes"); return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module) { if (module->data != NULL) json_decref((json_t*) module->data); return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/demo/000077500000000000000000000000001413423160300164675ustar00rootroot00000000000000yara-4.1.3/libyara/modules/demo/demo.c000066400000000000000000000037751413423160300175730ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #define MODULE_NAME demo begin_declarations declare_string("greeting"); end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { set_string("Hello World!", module_object, "greeting"); return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/dex/000077500000000000000000000000001413423160300163235ustar00rootroot00000000000000yara-4.1.3/libyara/modules/dex/dex.c000066400000000000000000001246031413423160300172550ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define MODULE_NAME dex // DEX File layout information: // https://source.android.com/devices/tech/dalvik/dex-format define_function(has_method_string) { SIZED_STRING* parsed_name; SIZED_STRING* method_name = sized_string_argument(1); YR_OBJECT* module = module(); int64_t number_of_methods = get_integer(module, "number_of_methods"); if (number_of_methods == YR_UNDEFINED) return_integer(YR_UNDEFINED); for (int i = 0; i < number_of_methods; i++) { parsed_name = get_string(module, "method[%i].name", i); if (parsed_name != NULL && strcmp(parsed_name->c_string, method_name->c_string) == 0) { return_integer(1); } } return_integer(0); } define_function(has_method_and_class_string) { SIZED_STRING* parsed_class; SIZED_STRING* parsed_name; SIZED_STRING* class_name = sized_string_argument(1); SIZED_STRING* method_name = sized_string_argument(2); YR_OBJECT* module = module(); int64_t number_of_methods = get_integer(module, "number_of_methods"); if (number_of_methods == YR_UNDEFINED) return_integer(YR_UNDEFINED); for (int i = 0; i < number_of_methods; i++) { parsed_class = get_string(module, "method[%i].class_name", i); if (parsed_class != NULL && strcmp(parsed_class->c_string, class_name->c_string) != 0) { continue; } parsed_name = get_string(module, "method[%i].name", i); if (parsed_name != NULL && strcmp(parsed_name->c_string, method_name->c_string) == 0) { return_integer(1); } } return_integer(0); } define_function(has_method_regexp) { SIZED_STRING* parsed_name; RE* regex = regexp_argument(1); YR_OBJECT* module = module(); int64_t number_of_methods = get_integer(module, "number_of_methods"); if (number_of_methods == YR_UNDEFINED) return_integer(YR_UNDEFINED); for (int i = 0; i < number_of_methods; i++) { parsed_name = get_string(module, "method[%i].name", i); if (parsed_name != NULL && yr_re_match(scan_context(), regex, parsed_name->c_string) != -1) { return_integer(1); } } return_integer(0); } define_function(has_method_and_class_regexp) { SIZED_STRING* parsed_class; SIZED_STRING* parsed_name; RE* class_regex = regexp_argument(1); RE* name_regex = regexp_argument(2); YR_OBJECT* module = module(); int64_t number_of_methods = get_integer(module, "number_of_methods"); if (number_of_methods == YR_UNDEFINED) return_integer(YR_UNDEFINED); for (int i = 0; i < number_of_methods; i++) { parsed_class = get_string(module, "method[%i].class_name", i); if (parsed_class != NULL && yr_re_match(scan_context(), class_regex, parsed_class->c_string) == -1) { continue; } parsed_name = get_string(module, "method[%i].name", i); if (parsed_name != NULL && yr_re_match(scan_context(), name_regex, parsed_name->c_string) != -1) { return_integer(1); } } return_integer(0); } define_function(has_class_string) { SIZED_STRING* parsed_class; SIZED_STRING* class_name = sized_string_argument(1); YR_OBJECT* module = module(); int64_t number_of_methods = get_integer(module, "number_of_methods"); if (number_of_methods == YR_UNDEFINED) return_integer(YR_UNDEFINED); for (int i = 0; i < number_of_methods; i++) { parsed_class = get_string(module, "method[%i].class_name", i); if (parsed_class != NULL && strcmp(parsed_class->c_string, class_name->c_string) == 0) { return_integer(1); } } return_integer(0); } define_function(has_class_regexp) { SIZED_STRING* parsed_class; RE* regex = regexp_argument(1); YR_OBJECT* module = module(); int64_t number_of_methods = get_integer(module, "number_of_methods"); if (number_of_methods == YR_UNDEFINED) return_integer(YR_UNDEFINED); for (int i = 0; i < number_of_methods; i++) { parsed_class = get_string(module, "method[%i].class_name", i); if (parsed_class != NULL && yr_re_match(scan_context(), regex, parsed_class->c_string) != -1) { return_integer(1); } } return_integer(0); } begin_declarations declare_string("DEX_FILE_MAGIC_035"); declare_string("DEX_FILE_MAGIC_036"); declare_string("DEX_FILE_MAGIC_037"); declare_string("DEX_FILE_MAGIC_038"); declare_string("DEX_FILE_MAGIC_039"); declare_integer("ENDIAN_CONSTANT"); declare_integer("REVERSE_ENDIAN_CONSTANT"); declare_integer("NO_INDEX"); declare_integer("ACC_PUBLIC"); declare_integer("ACC_PRIVATE"); declare_integer("ACC_PROTECTED"); declare_integer("ACC_STATIC"); declare_integer("ACC_FINAL"); declare_integer("ACC_SYNCHRONIZED"); declare_integer("ACC_VOLATILE"); declare_integer("ACC_BRIDGE"); declare_integer("ACC_TRANSIENT"); declare_integer("ACC_VARARGS"); declare_integer("ACC_NATIVE"); declare_integer("ACC_INTERFACE"); declare_integer("ACC_ABSTRACT"); declare_integer("ACC_STRICT"); declare_integer("ACC_SYNTHETIC"); declare_integer("ACC_ANNOTATION"); declare_integer("ACC_ENUM"); declare_integer("ACC_CONSTRUCTOR"); declare_integer("ACC_DECLARED_SYNCHRONIZED"); declare_integer("TYPE_HEADER_ITEM"); declare_integer("TYPE_STRING_ID_ITEM"); declare_integer("TYPE_TYPE_ID_ITEM"); declare_integer("TYPE_PROTO_ID_ITEM"); declare_integer("TYPE_FIELD_ID_ITEM"); declare_integer("TYPE_METHOD_ID_ITEM"); declare_integer("TYPE_CLASS_DEF_ITEM"); declare_integer("TYPE_CALL_SITE_ID_ITEM"); declare_integer("TYPE_METHOD_HANDLE_ITEM"); declare_integer("TYPE_MAP_LIST"); declare_integer("TYPE_TYPE_LIST"); declare_integer("TYPE_ANNOTATION_SET_REF_LIST"); declare_integer("TYPE_ANNOTATION_SET_ITEM"); declare_integer("TYPE_CLASS_DATA_ITEM"); declare_integer("TYPE_CODE_ITEM"); declare_integer("TYPE_STRING_DATA_ITEM"); declare_integer("TYPE_DEBUG_INFO_ITEM"); declare_integer("TYPE_ANNOTATION_ITEM"); declare_integer("TYPE_ENCODED_ARRAY_ITEM"); declare_integer("TYPE_ANNOTATIONS_DIRECTORY_ITEM"); begin_struct("header") declare_string("magic"); declare_integer("checksum"); declare_string("signature"); declare_integer("file_size"); declare_integer("header_size"); declare_integer("endian_tag"); declare_integer("link_size"); declare_integer("link_offset"); declare_integer("map_offset"); declare_integer("string_ids_size"); declare_integer("string_ids_offset"); declare_integer("type_ids_size"); declare_integer("type_ids_offset"); declare_integer("proto_ids_size"); declare_integer("proto_ids_offset"); declare_integer("field_ids_size"); declare_integer("field_ids_offset"); declare_integer("method_ids_size"); declare_integer("method_ids_offset"); declare_integer("class_defs_size"); declare_integer("class_defs_offset"); declare_integer("data_size"); declare_integer("data_offset"); end_struct("header") begin_struct_array("string_ids") declare_integer("offset"); declare_integer("size"); declare_string("value"); end_struct_array("string_ids") begin_struct_array("type_ids") declare_integer("descriptor_idx"); end_struct_array("type_ids") begin_struct_array("proto_ids") declare_integer("shorty_idx"); declare_integer("return_type_idx"); declare_integer("parameters_offset"); end_struct_array("proto_ids") begin_struct_array("field_ids") declare_integer("class_idx"); declare_integer("type_idx"); declare_integer("name_idx"); end_struct_array("field_ids") begin_struct_array("method_ids") declare_integer("class_idx"); declare_integer("proto_idx"); declare_integer("name_idx"); end_struct_array("method_ids") begin_struct_array("class_defs") declare_integer("class_idx"); declare_integer("access_flags"); declare_integer("super_class_idx"); declare_integer("interfaces_offset"); declare_integer("source_file_idx"); declare_integer("annotations_offset"); declare_integer("class_data_offset"); declare_integer("static_values_offset"); end_struct_array("class_defs") begin_struct_array("class_data_item") declare_integer("static_fields_size"); declare_integer("instance_fields_size"); declare_integer("direct_methods_size"); declare_integer("virtual_methods_size"); end_struct_array("class_data_item") begin_struct("map_list") declare_integer("size"); begin_struct_array("map_item") declare_integer("type"); declare_integer("unused"); declare_integer("size"); declare_integer("offset"); end_struct_array("map_item"); end_struct("map_list") declare_integer("number_of_fields"); begin_struct_array("field") declare_string("class_name"); declare_string("name"); declare_string("proto"); declare_integer("field_idx_diff"); declare_integer("access_flags"); end_struct_array("field") declare_function("has_method", "s", "i", has_method_string); declare_function("has_method", "ss", "i", has_method_and_class_string); declare_function("has_method", "r", "i", has_method_regexp); declare_function("has_method", "rr", "i", has_method_and_class_regexp); declare_function("has_class", "s", "i", has_class_string); declare_function("has_class", "r", "i", has_class_regexp); declare_integer("number_of_methods"); begin_struct_array("method") declare_string("class_name"); declare_string("name"); declare_string("proto"); declare_integer("direct"); declare_integer("virtual"); declare_integer("method_idx_diff"); declare_integer("access_flags"); declare_integer("code_off"); begin_struct("code_item") declare_integer("registers_size"); declare_integer("ins_size"); declare_integer("outs_size"); declare_integer("tries_size"); declare_integer("debug_info_off"); declare_integer("insns_size"); declare_string("insns"); declare_integer("padding"); begin_struct("tries") end_struct("tries"); begin_struct_array("handlers") end_struct_array("handlers"); end_struct("code_item") end_struct_array("method") end_declarations // https://android.googlesource.com/platform/dalvik/+/android-4.4.2_r2/libdex/Leb128.cpp static int32_t read_uleb128(const uint8_t* pStream, uint32_t* size) { const uint8_t* ptr = pStream; int32_t result = *(ptr++); *size = *size + 1; if (result > 0x7f) { int cur = *(ptr++); *size = *size + 1; result = (result & 0x7f) | ((cur & 0x7f) << 7); if (cur > 0x7f) { cur = *(ptr++); *size = *size + 1; result |= (cur & 0x7f) << 14; if (cur > 0x7f) { cur = *(ptr++); *size = *size + 1; result |= (cur & 0x7f) << 21; if (cur > 0x7f) { /* * Note: We don't check to see if cur is out of * range here, meaning we tolerate garbage in the * high four-order bits. */ cur = *(ptr++); *size = *size + 1; result |= cur << 28; } } } } return result; } static int64_t dex_get_integer( YR_OBJECT* object, const char* pattern, int64_t index) { if (index == YR_UNDEFINED) return YR_UNDEFINED; // Impose a reasonably large limit to table indexes. if (index > 0x80000) return YR_UNDEFINED; return get_integer(object, pattern, (int) index); } static SIZED_STRING* dex_get_string( YR_OBJECT* object, const char* pattern, int64_t index) { if (index == YR_UNDEFINED) return NULL; // Impose a reasonably large limit to table indexes. if (index > 0x80000) return NULL; return get_string(object, pattern, (int) index); } dex_header_t* dex_get_header(const uint8_t* data, size_t data_size) { dex_header_t* dex_header; if (data_size < sizeof(dex_header_t)) return NULL; // Check if we have a valid DEX file dex_header = (dex_header_t*) data; if (memcmp(dex_header->magic, DEX_FILE_MAGIC_035, 8) != 0 && memcmp(dex_header->magic, DEX_FILE_MAGIC_036, 8) != 0 && memcmp(dex_header->magic, DEX_FILE_MAGIC_037, 8) != 0 && memcmp(dex_header->magic, DEX_FILE_MAGIC_038, 8) != 0 && memcmp(dex_header->magic, DEX_FILE_MAGIC_039, 8) != 0) { return NULL; } return dex_header; } void dex_parse_header(dex_header_t* dex_header, YR_OBJECT* module_object) { set_sized_string( (char*) dex_header->magic, strnlen((char*) dex_header->magic, 8 * sizeof(char)), module_object, "header.magic"); set_integer( yr_le32toh(dex_header->checksum), module_object, "header.checksum"); set_sized_string( (char*) dex_header->signature, strnlen((char*) dex_header->signature, 20 * sizeof(char)), module_object, "header.signature"); set_integer( yr_le32toh(dex_header->file_size), module_object, "header.file_size"); set_integer( yr_le32toh(dex_header->header_size), module_object, "header.header_size"); set_integer( yr_le32toh(dex_header->endian_tag), module_object, "header.endian_tag"); set_integer( yr_le32toh(dex_header->link_size), module_object, "header.link_size"); set_integer( yr_le32toh(dex_header->link_offset), module_object, "header.link_offset"); set_integer( yr_le32toh(dex_header->map_offset), module_object, "header.map_offset"); set_integer( yr_le32toh(dex_header->string_ids_size), module_object, "header.string_ids_size"); set_integer( yr_le32toh(dex_header->string_ids_offset), module_object, "header.string_ids_offset"); set_integer( yr_le32toh(dex_header->type_ids_size), module_object, "header.type_ids_size"); set_integer( yr_le32toh(dex_header->type_ids_offset), module_object, "header.type_ids_offset"); set_integer( yr_le32toh(dex_header->proto_ids_size), module_object, "header.proto_ids_size"); set_integer( yr_le32toh(dex_header->proto_ids_offset), module_object, "header.proto_ids_offset"); set_integer( yr_le32toh(dex_header->field_ids_size), module_object, "header.field_ids_size"); set_integer( yr_le32toh(dex_header->field_ids_offset), module_object, "header.field_ids_offset"); set_integer( yr_le32toh(dex_header->method_ids_size), module_object, "header.method_ids_size"); set_integer( yr_le32toh(dex_header->method_ids_offset), module_object, "header.method_ids_offset"); set_integer( yr_le32toh(dex_header->class_defs_size), module_object, "header.class_defs_size"); set_integer( yr_le32toh(dex_header->class_defs_offset), module_object, "header.class_defs_offset"); set_integer( yr_le32toh(dex_header->data_size), module_object, "header.data_size"); set_integer( yr_le32toh(dex_header->data_offset), module_object, "header.data_offset"); } uint32_t load_encoded_field( DEX* dex, size_t start_offset, uint32_t* previous_field_idx, int index_encoded_field, int static_field, int instance_field) { #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse encoded field start_offset:0x%zx\n", start_offset); #endif if (!fits_in_dex(dex, dex->data + start_offset, sizeof(uint32_t) * 2)) return 0; uint32_t current_size = 0; encoded_field_t encoded_field; encoded_field.field_idx_diff = (uint32_t) read_uleb128( (dex->data + start_offset + current_size), ¤t_size); encoded_field.access_flags = (uint32_t) read_uleb128( (dex->data + start_offset + current_size), ¤t_size); set_integer( encoded_field.field_idx_diff, dex->object, "field[%i].field_idx_diff", index_encoded_field); set_integer( encoded_field.access_flags, dex->object, "field[%i].access_flags", index_encoded_field); set_integer( static_field, dex->object, "field[%i].static", index_encoded_field); set_integer( instance_field, dex->object, "field[%i].instance", index_encoded_field); *previous_field_idx = encoded_field.field_idx_diff + *previous_field_idx; #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tEncoded field field_idx:0x%x field_idx_diff:0x%x " "access_flags:0x%x\n", *previous_field_idx, encoded_field.field_idx_diff, encoded_field.access_flags); #endif int64_t name_idx = dex_get_integer( dex->object, "field_ids[%i].name_idx", *previous_field_idx); if (name_idx == YR_UNDEFINED) return 0; SIZED_STRING* field_name = dex_get_string( dex->object, "string_ids[%i].value", name_idx); if (field_name != NULL) { #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tFIELD_NAME %s NAME_IDX 0x%x\n", field_name->c_string, name_idx); #endif set_sized_string( field_name->c_string, field_name->length, dex->object, "field[%i].name", index_encoded_field); } int64_t class_idx = dex_get_integer( dex->object, "field_ids[%i].class_idx", *previous_field_idx); int64_t descriptor_idx = dex_get_integer( dex->object, "type_ids[%i].descriptor_idx", class_idx); SIZED_STRING* class_name = dex_get_string( dex->object, "string_ids[%i].value", descriptor_idx); if (class_name != NULL) { #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tCLASS_NAME %s CLASS_IDX 0x%x DESCRIPTOR_IDX 0x%x\n", class_name->c_string, class_idx, descriptor_idx); #endif set_sized_string( class_name->c_string, class_name->length, dex->object, "field[%i].class_name", index_encoded_field); } int type_idx = dex_get_integer( dex->object, "field_ids[%i].type_idx", *previous_field_idx); int shorty_idx = dex_get_integer( dex->object, "type_ids[%i].descriptor_idx", type_idx); SIZED_STRING* proto_name = dex_get_string( dex->object, "string_ids[%i].value", shorty_idx); if (proto_name != NULL) { #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tPROTO_NAME %s TYPE_IDX 0x%x SHORTY_IDX 0x%x\n", proto_name->c_string, type_idx, shorty_idx); #endif set_sized_string( proto_name->c_string, proto_name->length, dex->object, "field[%i].proto", index_encoded_field); } return current_size; } uint32_t load_encoded_method( DEX* dex, size_t start_offset, uint32_t* previous_method_idx, int index_encoded_method, int direct_method, int virtual_method) { #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse encoded method start_offset:0x%zx\n", start_offset); #endif if (!fits_in_dex(dex, dex->data + start_offset, sizeof(uint32_t) * 3)) return 0; uint32_t current_size = 0; encoded_method_t encoded_method; encoded_method.method_idx_diff = (uint32_t) read_uleb128( (dex->data + start_offset + current_size), ¤t_size); encoded_method.access_flags = (uint32_t) read_uleb128( (dex->data + start_offset + current_size), ¤t_size); encoded_method.code_off = (uint32_t) read_uleb128( (dex->data + start_offset + current_size), ¤t_size); set_integer( encoded_method.method_idx_diff, dex->object, "method[%i].method_idx_diff", index_encoded_method); set_integer( encoded_method.access_flags, dex->object, "method[%i].access_flags", index_encoded_method); set_integer( encoded_method.code_off, dex->object, "method[%i].code_off", index_encoded_method); set_integer( direct_method, dex->object, "method[%i].direct", index_encoded_method); set_integer( virtual_method, dex->object, "method[%i].virtual", index_encoded_method); *previous_method_idx = encoded_method.method_idx_diff + *previous_method_idx; int64_t name_idx = dex_get_integer( dex->object, "method_ids[%i].name_idx", *previous_method_idx); if (name_idx == YR_UNDEFINED) return 0; #ifdef DEBUG_DEX_MODULE printf("[DEX]\tNAME_IDX 0x%x\n", name_idx); #endif #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tEncoded method method_idx:0x%x method_idx_diff:0x%x " "access_flags:0x%x code_off:0x%x\n", *previous_method_idx, encoded_method.method_idx_diff, encoded_method.access_flags, encoded_method.code_off); #endif SIZED_STRING* method_name = dex_get_string( dex->object, "string_ids[%i].value", name_idx); if (method_name != NULL) { #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tMETHOD_NAME %s NAME_IDX 0x%x\n", method_name->c_string, name_idx); #endif set_sized_string( method_name->c_string, method_name->length, dex->object, "method[%i].name", index_encoded_method); } int64_t class_idx = dex_get_integer( dex->object, "method_ids[%i].class_idx", *previous_method_idx); int64_t descriptor_idx = dex_get_integer( dex->object, "type_ids[%i].descriptor_idx", class_idx); SIZED_STRING* class_name = dex_get_string( dex->object, "string_ids[%i].value", descriptor_idx); if (class_name != NULL) { #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tCLASS_NAME %s CLASS_IDX 0x%x DESCRIPTOR_IDX:0x%x\n", class_name->c_string, class_idx, descriptor_idx); #endif set_sized_string( class_name->c_string, class_name->length, dex->object, "method[%i].class_name", index_encoded_method); } int64_t proto_idx = dex_get_integer( dex->object, "method_ids[%i].proto_idx", *previous_method_idx); int64_t shorty_idx = dex_get_integer( dex->object, "proto_ids[%i].shorty_idx", proto_idx); SIZED_STRING* proto_name = dex_get_string( dex->object, "string_ids[%i].value", shorty_idx); if (proto_name != NULL) { #ifdef DEBUG_DEX_MODULE printf( "[DEX]\tPROTO_NAME %s CLASS_IDX 0x%x DESCRIPTOR_IDX:0x%x\n", proto_name->c_string, class_idx, descriptor_idx); #endif set_sized_string( proto_name->c_string, proto_name->length, dex->object, "method[%i].proto", index_encoded_method); } if (encoded_method.code_off != 0) { #ifdef DEBUG_DEX_MODULE printf("[DEX]\t\tParse CODE item\n"); #endif if (struct_fits_in_dex( dex, dex->data + encoded_method.code_off, sizeof(code_item_t))) { code_item_t* code_item = (code_item_t*) (dex->data + encoded_method.code_off); set_integer( code_item->registers_size, dex->object, "method[%i].code_item.registers_size", index_encoded_method); set_integer( code_item->ins_size, dex->object, "method[%i].code_item.ins_size", index_encoded_method); set_integer( code_item->outs_size, dex->object, "method[%i].code_item.outs_size", index_encoded_method); set_integer( code_item->tries_size, dex->object, "method[%i].code_item.tries_size", index_encoded_method); set_integer( code_item->debug_info_off, dex->object, "method[%i].code_item.debug_info_off", index_encoded_method); set_integer( code_item->insns_size, dex->object, "method[%i].code_item.insns_size", index_encoded_method); if (fits_in_dex( dex, dex->data + encoded_method.code_off + sizeof(code_item_t), code_item->insns_size * 2)) { set_sized_string( (const char*) (dex->data + encoded_method.code_off + sizeof(code_item_t)), code_item->insns_size * 2, dex->object, "method[%i].code_item.insns", index_encoded_method); } } } return current_size; } void dex_parse(DEX* dex, uint64_t base_address) { dex_header_t* dex_header; int i, j; uint32_t uleb128_size = 0; uint32_t new_size = 0; uint32_t index_class_data_item = 0; uint32_t index_encoded_method = 0; uint32_t index_encoded_field = 0; if (!struct_fits_in_dex(dex, dex->data, dex_header_t)) return; dex_parse_header(dex->header, dex->object); dex_header = dex->header; if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->string_ids_offset), yr_le32toh(dex_header->string_ids_size) * sizeof(string_id_item_t))) return; #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse STRING ID section\n"); #endif // Get information about the String ID section for (i = 0; i < yr_le32toh(dex_header->string_ids_size); i++) { string_id_item_t* string_id_item = (string_id_item_t*) (dex->data + yr_le32toh(dex_header->string_ids_offset) + i * sizeof(string_id_item_t)); #ifdef DEBUG_DEX_MODULE printf( "[DEX] STRING ID item data_offset:0x%x\n", yr_le32toh(string_id_item->string_data_offset)); #endif if (!fits_in_dex( dex, dex->data + yr_le32toh(string_id_item->string_data_offset), sizeof(uint32_t))) continue; uint32_t value = (uint32_t) read_uleb128( (dex->data + yr_le32toh(string_id_item->string_data_offset)), &uleb128_size); #ifdef DEBUG_DEX_MODULE printf("[DEX] STRING ID item size:0x%x\n", value); #endif if (!fits_in_dex( dex, dex->data + yr_le32toh(string_id_item->string_data_offset), value)) continue; set_integer( yr_le32toh(string_id_item->string_data_offset), dex->object, "string_ids[%i].offset", i); set_integer(value, dex->object, "string_ids[%i].size", i); set_sized_string( (const char*) (( dex->data + yr_le32toh(string_id_item->string_data_offset) + 1)), value, dex->object, "string_ids[%i].value", i); } if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->type_ids_offset), yr_le32toh(dex_header->type_ids_size) * sizeof(type_id_item_t))) return; #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse TYPE ID section\n"); #endif // Get information about the Type ID section for (i = 0; i < yr_le32toh(dex_header->type_ids_size); i++) { type_id_item_t* type_id_item = (type_id_item_t*) (dex->data + yr_le32toh(dex_header->type_ids_offset) + i * sizeof(type_id_item_t)); set_integer( yr_le32toh(type_id_item->descriptor_idx), dex->object, "type_ids[%i].descriptor_idx", i); } if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->proto_ids_offset), yr_le32toh(dex_header->proto_ids_size) * sizeof(proto_id_item_t))) return; #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse PROTO ID section\n"); #endif // Get information about the Proto ID section for (i = 0; i < yr_le32toh(dex_header->proto_ids_size); i++) { proto_id_item_t* proto_id_item = (proto_id_item_t*) (dex->data + yr_le32toh(dex_header->proto_ids_offset) + i * sizeof(proto_id_item_t)); set_integer( yr_le32toh(proto_id_item->shorty_idx), dex->object, "proto_ids[%i].shorty_idx", i); set_integer( yr_le32toh(proto_id_item->return_type_idx), dex->object, "proto_ids[%i].return_type_idx", i); set_integer( yr_le32toh(proto_id_item->parameters_offset), dex->object, "proto_ids[%i].parameters_offset", i); } if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->field_ids_offset), yr_le32toh(dex_header->field_ids_size) * sizeof(field_id_item_t))) return; #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse FIELD ID section\n"); #endif // Get information about the Field ID section for (i = 0; i < yr_le32toh(dex_header->field_ids_size); i++) { field_id_item_t* field_id_item = (field_id_item_t*) (dex->data + yr_le32toh(dex_header->field_ids_offset) + i * sizeof(field_id_item_t)); set_integer( yr_le16toh(field_id_item->class_idx), dex->object, "field_ids[%i].class_idx", i); set_integer( yr_le16toh(field_id_item->type_idx), dex->object, "field_ids[%i].type_idx", i); set_integer( yr_le32toh(field_id_item->name_idx), dex->object, "field_ids[%i].name_idx", i); } if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->method_ids_offset), yr_le32toh(dex_header->method_ids_size) * sizeof(method_id_item_t))) return; #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse METHOD ID section\n"); #endif // Get information about the Method ID section for (i = 0; i < yr_le32toh(dex_header->method_ids_size); i++) { method_id_item_t* method_id_item = (method_id_item_t*) (dex->data + yr_le32toh(dex_header->method_ids_offset) + i * sizeof(method_id_item_t)); set_integer( yr_le16toh(method_id_item->class_idx), dex->object, "method_ids[%i].class_idx", i); set_integer( yr_le16toh(method_id_item->proto_idx), dex->object, "method_ids[%i].proto_idx", i); set_integer( yr_le32toh(method_id_item->name_idx), dex->object, "method_ids[%i].name_idx", i); } #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse MAP List ID section\n"); #endif // Get information about the Map List ID section if (yr_le32toh(dex_header->map_offset) != 0 && fits_in_dex( dex, dex->data + yr_le32toh(dex_header->map_offset), sizeof(uint32_t))) { uint32_t* map_list_size = (uint32_t*) (dex->data + yr_le32toh(dex_header->map_offset)); set_integer(yr_le32toh(*map_list_size), dex->object, "map_list.size"); if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->map_offset), sizeof(uint32_t) + yr_le32toh(*map_list_size) * sizeof(map_item_t))) return; for (i = 0; i < yr_le32toh(*map_list_size); i++) { map_item_t* map_item = (map_item_t*) (dex->data + yr_le32toh(dex_header->map_offset) + sizeof(uint32_t) + i * sizeof(map_item_t)); set_integer( yr_le16toh(map_item->type), dex->object, "map_list.map_item[%i].type", i); set_integer( yr_le16toh(map_item->unused), dex->object, "map_list.map_item[%i].unused", i); set_integer( yr_le32toh(map_item->size), dex->object, "map_list.map_item[%i].size", i); set_integer( yr_le32toh(map_item->offset), dex->object, "map_list.map_item[%i].offset", i); } } if (!fits_in_dex( dex, dex->data + yr_le32toh(dex_header->class_defs_offset), yr_le32toh(dex_header->class_defs_size) * sizeof(class_id_item_t))) return; #ifdef DEBUG_DEX_MODULE printf("[DEX] Parse CLASS ID section\n"); #endif // Get information about the Class ID section for (i = 0; i < yr_le32toh(dex_header->class_defs_size); i++) { class_id_item_t* class_id_item = (class_id_item_t*) (dex->data + yr_le32toh(dex_header->class_defs_offset) + i * sizeof(class_id_item_t)); #ifdef DEBUG_DEX_MODULE printf( "[DEX] CLASS ID item class_idx:0x%x access_flags:0x%x " "super_class_idx:0x%x interfaces_offset:0x%x source_file_idx:0x%x " "annotations_offset:0x%x class_data_offset:0x%x " "static_values_offset:0x%x\n", yr_le32toh(class_id_item->class_idx), yr_le32toh(class_id_item->access_flags), yr_le32toh(class_id_item->super_class_idx), yr_le32toh(class_id_item->interfaces_offset), yr_le32toh(class_id_item->source_file_idx), yr_le32toh(class_id_item->annotations_offset), yr_le32toh(class_id_item->class_data_offset), yr_le32toh(class_id_item->static_values_offset)); #endif set_integer( yr_le32toh(class_id_item->class_idx), dex->object, "class_defs[%i].class_idx", i); set_integer( yr_le32toh(class_id_item->access_flags), dex->object, "class_defs[%i].access_flags", i); set_integer( yr_le32toh(class_id_item->super_class_idx), dex->object, "class_defs[%i].super_class_idx", i); set_integer( yr_le32toh(class_id_item->interfaces_offset), dex->object, "class_defs[%i].interfaces_offset", i); set_integer( yr_le32toh(class_id_item->source_file_idx), dex->object, "class_defs[%i].source_file_idx", i); set_integer( yr_le32toh(class_id_item->annotations_offset), dex->object, "class_defs[%i].annotations_offset", i); set_integer( yr_le32toh(class_id_item->class_data_offset), dex->object, "class_defs[%i].class_data_offset", i); set_integer( yr_le32toh(class_id_item->static_values_offset), dex->object, "class_defs[%i].static_values_offset", i); if (yr_le32toh(class_id_item->class_data_offset) != 0) { class_data_item_t class_data_item; if (!fits_in_dex( dex, dex->data + yr_le32toh(class_id_item->class_data_offset), 4 * sizeof(uint32_t))) return; uleb128_size = 0; class_data_item.static_fields_size = (uint32_t) read_uleb128( (dex->data + yr_le32toh(class_id_item->class_data_offset)), &uleb128_size); class_data_item.instance_fields_size = (uint32_t) read_uleb128( (dex->data + yr_le32toh(class_id_item->class_data_offset) + uleb128_size), &uleb128_size); class_data_item.direct_methods_size = (uint32_t) read_uleb128( (dex->data + yr_le32toh(class_id_item->class_data_offset) + uleb128_size), &uleb128_size); class_data_item.virtual_methods_size = (uint32_t) read_uleb128( (dex->data + yr_le32toh(class_id_item->class_data_offset) + uleb128_size), &uleb128_size); set_integer( class_data_item.static_fields_size, dex->object, "class_data_item[%i].static_fields_size", index_class_data_item); set_integer( class_data_item.instance_fields_size, dex->object, "class_data_item[%i].instance_fields_size", index_class_data_item); set_integer( class_data_item.direct_methods_size, dex->object, "class_data_item[%i].direct_methods_size", index_class_data_item); set_integer( class_data_item.virtual_methods_size, dex->object, "class_data_item[%i].virtual_methods_size", index_class_data_item); #ifdef DEBUG_DEX_MODULE printf("[DEX] CLASS DATA item static fields\n"); #endif uint32_t previous_field_idx = 0; for (j = 0; j < class_data_item.static_fields_size; j++) { new_size = load_encoded_field( dex, yr_le32toh(class_id_item->class_data_offset) + uleb128_size, &previous_field_idx, index_encoded_field, 1, 0); // If the current field isn't parsed the other fields aren't likely to // parse. if (new_size == 0) break; uleb128_size += new_size; index_encoded_field += 1; } #ifdef DEBUG_DEX_MODULE printf("[DEX] CLASS DATA item instance fields\n"); #endif previous_field_idx = 0; for (j = 0; j < class_data_item.instance_fields_size; j++) { new_size = load_encoded_field( dex, yr_le32toh(class_id_item->class_data_offset) + uleb128_size, &previous_field_idx, index_encoded_field, 0, 1); // If the current field isn't parsed the other fields aren't likely to // parse. if (new_size == 0) break; uleb128_size += new_size; index_encoded_field += 1; } #ifdef DEBUG_DEX_MODULE printf("[DEX] CLASS DATA item direct methods\n"); #endif uint32_t previous_method_idx = 0; for (j = 0; j < class_data_item.direct_methods_size; j++) { new_size = load_encoded_method( dex, yr_le32toh(class_id_item->class_data_offset) + uleb128_size, &previous_method_idx, index_encoded_method, 1, 0); // If the current field isn't parsed the other fields aren't likely to // parse. if (new_size == 0) break; uleb128_size += new_size; index_encoded_method += 1; } #ifdef DEBUG_DEX_MODULE printf("[DEX] CLASS DATA item virtual methods\n"); #endif previous_method_idx = 0; for (j = 0; j < class_data_item.virtual_methods_size; j++) { new_size = load_encoded_method( dex, yr_le32toh(class_id_item->class_data_offset) + uleb128_size, &previous_method_idx, index_encoded_method, 0, 1); // If the current field isn't parsed the other fields aren't likely to // parse. if (new_size == 0) break; uleb128_size += new_size; index_encoded_method += 1; } index_class_data_item++; } } set_integer(index_encoded_method, dex->object, "number_of_methods"); set_integer(index_encoded_field, dex->object, "number_of_fields"); } int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; dex_header_t* dex_header; set_string(DEX_FILE_MAGIC_035, module_object, "DEX_FILE_MAGIC_035"); set_string(DEX_FILE_MAGIC_036, module_object, "DEX_FILE_MAGIC_036"); set_string(DEX_FILE_MAGIC_037, module_object, "DEX_FILE_MAGIC_037"); set_string(DEX_FILE_MAGIC_038, module_object, "DEX_FILE_MAGIC_038"); set_string(DEX_FILE_MAGIC_039, module_object, "DEX_FILE_MAGIC_039"); set_integer(0x12345678, module_object, "ENDIAN_CONSTANT"); set_integer(0x78563412, module_object, "REVERSE_ENDIAN_CONSTANT"); set_integer(0xffffffff, module_object, "NO_INDEX"); set_integer(0x1, module_object, "ACC_PUBLIC"); set_integer(0x2, module_object, "ACC_PRIVATE"); set_integer(0x4, module_object, "ACC_PROTECTED"); set_integer(0x8, module_object, "ACC_STATIC"); set_integer(0x10, module_object, "ACC_FINAL"); set_integer(0x20, module_object, "ACC_SYNCHRONIZED"); set_integer(0x40, module_object, "ACC_VOLATILE"); set_integer(0x40, module_object, "ACC_BRIDGE"); set_integer(0x80, module_object, "ACC_TRANSIENT"); set_integer(0x80, module_object, "ACC_VARARGS"); set_integer(0x100, module_object, "ACC_NATIVE"); set_integer(0x200, module_object, "ACC_INTERFACE"); set_integer(0x400, module_object, "ACC_ABSTRACT"); set_integer(0x800, module_object, "ACC_STRICT"); set_integer(0x1000, module_object, "ACC_SYNTHETIC"); set_integer(0x2000, module_object, "ACC_ANNOTATION"); set_integer(0x4000, module_object, "ACC_ENUM"); set_integer(0x10000, module_object, "ACC_CONSTRUCTOR"); set_integer(0x20000, module_object, "ACC_DECLARED_SYNCHRONIZED"); set_integer(0x0000, module_object, "TYPE_HEADER_ITEM"); set_integer(0x0001, module_object, "TYPE_STRING_ID_ITEM"); set_integer(0x0002, module_object, "TYPE_TYPE_ID_ITEM"); set_integer(0x0003, module_object, "TYPE_PROTO_ID_ITEM"); set_integer(0x0004, module_object, "TYPE_FIELD_ID_ITEM"); set_integer(0x0005, module_object, "TYPE_METHOD_ID_ITEM"); set_integer(0x0006, module_object, "TYPE_CLASS_DEF_ITEM"); set_integer(0x0007, module_object, "TYPE_CALL_SITE_ID_ITEM"); set_integer(0x0008, module_object, "TYPE_METHOD_HANDLE_ITEM"); set_integer(0x1000, module_object, "TYPE_MAP_LIST"); set_integer(0x1001, module_object, "TYPE_TYPE_LIST"); set_integer(0x1002, module_object, "TYPE_ANNOTATION_SET_REF_LIST"); set_integer(0x1003, module_object, "TYPE_ANNOTATION_SET_ITEM"); set_integer(0x2000, module_object, "TYPE_CLASS_DATA_ITEM"); set_integer(0x2001, module_object, "TYPE_CODE_ITEM"); set_integer(0x2002, module_object, "TYPE_STRING_DATA_ITEM"); set_integer(0x2003, module_object, "TYPE_DEBUG_INFO_ITEM"); set_integer(0x2004, module_object, "TYPE_ANNOTATION_ITEM"); set_integer(0x2005, module_object, "TYPE_ENCODED_ARRAY_ITEM"); set_integer(0x2006, module_object, "TYPE_ANNOTATIONS_DIRECTORY_ITEM"); foreach_memory_block(iterator, block) { const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL) continue; dex_header = dex_get_header(block_data, block->size); if (dex_header != NULL) { DEX* dex = (DEX*) yr_malloc(sizeof(DEX)); if (dex == NULL) return ERROR_INSUFFICIENT_MEMORY; dex->data = block_data; dex->data_size = block->size; dex->object = module_object; dex->header = dex_header; module_object->data = dex; dex_parse(dex, block->base); break; } } return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { DEX* dex = (DEX*) module_object->data; if (dex == NULL) return ERROR_SUCCESS; yr_free(dex); return ERROR_SUCCESS; } #undef MODULE_NAME yara-4.1.3/libyara/modules/dotnet/000077500000000000000000000000001413423160300170405ustar00rootroot00000000000000yara-4.1.3/libyara/modules/dotnet/dotnet.c000066400000000000000000001464611413423160300205150ustar00rootroot00000000000000/* Copyright (c) 2015. The YARA Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #define MODULE_NAME dotnet char* pe_get_dotnet_string( PE* pe, const uint8_t* string_offset, DWORD string_index) { size_t remaining; char* start; char* eos; // Start of string must be within boundary if (!(string_offset + string_index >= pe->data && string_offset + string_index < pe->data + pe->data_size)) return NULL; // Calculate how much until end of boundary, don't scan past that. remaining = (pe->data + pe->data_size) - (string_offset + string_index); // Search for a NULL terminator from start of string, up to remaining. start = (char*) (string_offset + string_index); eos = (char*) memmem((void*) start, remaining, "\0", 1); if (eos == NULL) return eos; return start; } uint32_t max_rows(int count, ...) { va_list ap; int i; uint32_t biggest; uint32_t x; if (count == 0) return 0; va_start(ap, count); biggest = va_arg(ap, uint32_t); for (i = 1; i < count; i++) { x = va_arg(ap, uint32_t); biggest = (x > biggest) ? x : biggest; } va_end(ap); return biggest; } void dotnet_parse_guid( PE* pe, int64_t metadata_root, PSTREAM_HEADER guid_header) { // GUIDs are 16 bytes each, converted to hex format plus separators and NULL. char guid[37]; int i = 0; const uint8_t* guid_offset = pe->data + metadata_root + yr_le32toh(guid_header->Offset); DWORD guid_size = yr_le32toh(guid_header->Size); // Limit the number of GUIDs to 16. guid_size = yr_min(guid_size, 256); // Parse GUIDs if we have them. GUIDs are 16 bytes each. while (guid_size >= 16 && fits_in_pe(pe, guid_offset, 16)) { sprintf( guid, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", yr_le32toh(*(uint32_t*) guid_offset), yr_le16toh(*(uint16_t*) (guid_offset + 4)), yr_le16toh(*(uint16_t*) (guid_offset + 6)), *(guid_offset + 8), *(guid_offset + 9), *(guid_offset + 10), *(guid_offset + 11), *(guid_offset + 12), *(guid_offset + 13), *(guid_offset + 14), *(guid_offset + 15)); guid[(16 * 2) + 4] = '\0'; set_string(guid, pe->object, "guids[%i]", i); i++; guid_size -= 16; guid_offset += 16; } set_integer(i, pe->object, "number_of_guids"); } // Given an offset into a #US or #Blob stream, parse the entry at that position. // The offset is relative to the start of the PE file. BLOB_PARSE_RESULT dotnet_parse_blob_entry(PE* pe, const uint8_t* offset) { BLOB_PARSE_RESULT result; // Blob size is encoded in the first 1, 2 or 4 bytes of the blob. // // If the high bit is not set the length is encoded in one byte. // // If the high 2 bits are 10 (base 2) then the length is encoded in // the rest of the bits and the next byte. // // If the high 3 bits are 110 (base 2) then the length is encoded // in the rest of the bits and the next 3 bytes. // // See ECMA-335 II.24.2.4 for details. // Make sure we have at least one byte. if (!fits_in_pe(pe, offset, 1)) { result.size = 0; return result; } if ((*offset & 0x80) == 0x00) { result.length = (DWORD) *offset; result.size = 1; } else if ((*offset & 0xC0) == 0x80) { // Make sure we have one more byte. if (!fits_in_pe(pe, offset, 2)) { result.size = 0; return result; } // Shift remaining 6 bits left by 8 and OR in the remaining byte. result.length = ((*offset & 0x3F) << 8) | *(offset + 1); result.size = 2; } else if (offset + 4 < pe->data + pe->data_size && (*offset & 0xE0) == 0xC0) { // Make sure we have 3 more bytes. if (!fits_in_pe(pe, offset, 4)) { result.size = 0; return result; } result.length = ((*offset & 0x1F) << 24) | (*(offset + 1) << 16) | (*(offset + 2) << 8) | *(offset + 3); result.size = 4; } else { // Return a 0 size as an error. result.size = 0; return result; } // There is an additional terminal byte which is 0x01 under certain // conditions. The exact conditions are not relevant to our parsing but are // documented in ECMA-335 II.24.2.4. if (result.length > 0) result.length--; return result; } void dotnet_parse_us(PE* pe, int64_t metadata_root, PSTREAM_HEADER us_header) { BLOB_PARSE_RESULT blob_result; int i = 0; const uint32_t ush_sz = yr_le32toh(us_header->Size); const uint8_t* offset = pe->data + metadata_root + yr_le32toh(us_header->Offset); const uint8_t* end_of_header = offset + ush_sz; // Make sure the header size is larger than 0 and its end is not past the // end of PE. if (ush_sz == 0 || !fits_in_pe(pe, offset, ush_sz)) return; // The first entry MUST be single NULL byte. if (*offset != 0x00) return; offset++; while (offset < end_of_header) { blob_result = dotnet_parse_blob_entry(pe, offset); if (blob_result.size == 0) break; offset += blob_result.size; // Avoid empty strings, which usually happen as padding at the end of the // stream. if (blob_result.length > 0 && fits_in_pe(pe, offset, blob_result.length)) { set_sized_string( (char*) offset, blob_result.length, pe->object, "user_strings[%i]", i); offset += blob_result.length; i++; } } set_integer(i, pe->object, "number_of_user_strings"); } STREAMS dotnet_parse_stream_headers( PE* pe, int64_t offset, int64_t metadata_root, DWORD num_streams) { PSTREAM_HEADER stream_header; STREAMS headers; char* start; char* eos; char stream_name[DOTNET_STREAM_NAME_SIZE + 1]; unsigned int i; memset(&headers, '\0', sizeof(STREAMS)); stream_header = (PSTREAM_HEADER)(pe->data + offset); for (i = 0; i < num_streams; i++) { if (!struct_fits_in_pe(pe, stream_header, STREAM_HEADER)) break; start = (char*) stream_header->Name; if (!fits_in_pe(pe, start, DOTNET_STREAM_NAME_SIZE)) break; eos = (char*) memmem((void*) start, DOTNET_STREAM_NAME_SIZE, "\0", 1); if (eos == NULL) break; strncpy(stream_name, stream_header->Name, DOTNET_STREAM_NAME_SIZE); stream_name[DOTNET_STREAM_NAME_SIZE] = '\0'; set_string(stream_name, pe->object, "streams[%i].name", i); // Offset is relative to metadata_root. set_integer( metadata_root + yr_le32toh(stream_header->Offset), pe->object, "streams[%i].offset", i); set_integer( yr_le32toh(stream_header->Size), pe->object, "streams[%i].size", i); // Store necessary bits to parse these later. Not all tables will be // parsed, but are referenced from others. For example, the #Strings // stream is referenced from various tables in the #~ heap. // // #- is not documented but it represents unoptimized metadata stream. It // may contain additional tables such as FieldPtr, ParamPtr, MethodPtr or // PropertyPtr for indirect referencing. We already take into account these // tables and they do not interfere with anything we parse in this module. if ((strncmp(stream_name, "#~", 2) == 0 || strncmp(stream_name, "#-", 2) == 0) && headers.tilde == NULL) headers.tilde = stream_header; else if (strncmp(stream_name, "#GUID", 5) == 0) headers.guid = stream_header; else if (strncmp(stream_name, "#Strings", 8) == 0 && headers.string == NULL) headers.string = stream_header; else if (strncmp(stream_name, "#Blob", 5) == 0) headers.blob = stream_header; else if (strncmp(stream_name, "#US", 3) == 0 && headers.us == NULL) headers.us = stream_header; // Stream name is padded to a multiple of 4. stream_header = (PSTREAM_HEADER)( (uint8_t*) stream_header + sizeof(STREAM_HEADER) + strlen(stream_name) + 4 - (strlen(stream_name) % 4)); } set_integer(i, pe->object, "number_of_streams"); return headers; } // This is the second pass through the data for #~. The first pass collects // information on the number of rows for tables which have coded indexes. // This pass uses that information and the index_sizes to parse the tables // of interest. // // Because the indexes can vary in size depending upon the number of rows in // other tables it is impossible to use static sized structures. To deal with // this hardcode the sizes of each table based upon the documentation (for the // static sized portions) and use the variable sizes accordingly. void dotnet_parse_tilde_2( PE* pe, PTILDE_HEADER tilde_header, int64_t resource_base, int64_t metadata_root, ROWS rows, INDEX_SIZES index_sizes, PSTREAMS streams) { PMODULE_TABLE module_table; PASSEMBLY_TABLE assembly_table; PASSEMBLYREF_TABLE assemblyref_table; PFIELDRVA_TABLE fieldrva_table; PMANIFESTRESOURCE_TABLE manifestresource_table; PMODULEREF_TABLE moduleref_table; PCUSTOMATTRIBUTE_TABLE customattribute_table; PCONSTANT_TABLE constant_table; DWORD resource_size, implementation; char* name; char typelib[MAX_TYPELIB_SIZE + 1]; unsigned int i; int bit_check; int matched_bits = 0; int64_t resource_offset, field_offset; uint32_t row_size, row_count, counter; const uint8_t* string_offset; const uint8_t* blob_offset; uint32_t num_rows = 0; uint32_t valid_rows = 0; uint32_t* row_offset = NULL; uint8_t* table_offset = NULL; uint8_t* row_ptr = NULL; // These are pointers and row sizes for tables of interest to us for special // parsing. For example, we are interested in pulling out any CustomAttributes // that are GUIDs so we need to be able to walk these tables. To find GUID // CustomAttributes you need to walk the CustomAttribute table and look for // any row with a Parent that indexes into the Assembly table and Type indexes // into the MemberRef table. Then you follow the index into the MemberRef // table and check the Class to make sure it indexes into TypeRef table. If it // does you follow that index and make sure the Name is "GuidAttribute". If // all that is valid then you can take the Value from the CustomAttribute // table to find out the index into the Blob stream and parse that. // // Luckily we can abuse the fact that the order of the tables is guaranteed // consistent (though some may not exist, but if they do exist they must exist // in a certain order). The order is defined by their position in the Valid // member of the tilde_header structure. By the time we are parsing the // CustomAttribute table we have already recorded the location of the TypeRef // and MemberRef tables, so we can follow the chain back up from // CustomAttribute through MemberRef to TypeRef. uint8_t* typeref_ptr = NULL; uint8_t* memberref_ptr = NULL; uint32_t typeref_row_size = 0; uint32_t memberref_row_size = 0; uint8_t* typeref_row = NULL; uint8_t* memberref_row = NULL; DWORD type_index; DWORD class_index; BLOB_PARSE_RESULT blob_result; DWORD blob_index; DWORD blob_length; // These are used to determine the size of coded indexes, which are the // dynamically sized columns for some tables. The coded indexes are // documented in ECMA-335 Section II.24.2.6. uint8_t index_size, index_size2; // Number of rows is the number of bits set to 1 in Valid. // Should use this technique: // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan for (i = 0; i < 64; i++) valid_rows += ((yr_le64toh(tilde_header->Valid) >> i) & 0x01); row_offset = (uint32_t*) (tilde_header + 1); table_offset = (uint8_t*) row_offset; table_offset += sizeof(uint32_t) * valid_rows; #define DOTNET_STRING_INDEX(Name) \ index_sizes.string == 2 ? yr_le16toh(Name.Name_Short) \ : yr_le32toh(Name.Name_Long) string_offset = pe->data + metadata_root + yr_le32toh(streams->string->Offset); // Now walk again this time parsing out what we care about. for (bit_check = 0; bit_check < 64; bit_check++) { // If the Valid bit is not set for this table, skip it... if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01)) continue; // Make sure table_offset doesn't go crazy by inserting a large value // for num_rows. For example edc05e49dd3810be67942b983455fd43 sets a // large value for number of rows for the BIT_MODULE section. if (!fits_in_pe(pe, table_offset, 1)) return; num_rows = yr_le32toh(*(row_offset + matched_bits)); // Those tables which exist, but that we don't care about must be // skipped. // // Sadly, given the dynamic sizes of some columns we can not have well // defined structures for all tables and use them accordingly. To deal // with this manually move the table_offset pointer by the appropriate // number of bytes as described in the documentation for each table. // // The table structures are documented in ECMA-335 Section II.22. switch (bit_check) { case BIT_MODULE: module_table = (PMODULE_TABLE) table_offset; if (!struct_fits_in_pe(pe, module_table, MODULE_TABLE)) break; name = pe_get_dotnet_string( pe, string_offset, DOTNET_STRING_INDEX(module_table->Name)); if (name != NULL) set_string(name, pe->object, "module_name"); table_offset += (2 + index_sizes.string + (index_sizes.guid * 3)) * num_rows; break; case BIT_TYPEREF: row_count = max_rows( 4, yr_le32toh(rows.module), yr_le32toh(rows.moduleref), yr_le32toh(rows.assemblyref), yr_le32toh(rows.typeref)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; row_size = (index_size + (index_sizes.string * 2)); typeref_row_size = row_size; typeref_ptr = table_offset; table_offset += row_size * num_rows; break; case BIT_TYPEDEF: row_count = max_rows( 3, yr_le32toh(rows.typedef_), yr_le32toh(rows.typeref), yr_le32toh(rows.typespec)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; table_offset += (4 + (index_sizes.string * 2) + index_size + index_sizes.field + index_sizes.methoddef) * num_rows; break; case BIT_FIELDPTR: // This one is not documented in ECMA-335. table_offset += (index_sizes.field) * num_rows; break; case BIT_FIELD: table_offset += (2 + (index_sizes.string) + index_sizes.blob) * num_rows; break; case BIT_METHODDEFPTR: // This one is not documented in ECMA-335. table_offset += (index_sizes.methoddef) * num_rows; break; case BIT_METHODDEF: table_offset += (4 + 2 + 2 + index_sizes.string + index_sizes.blob + index_sizes.param) * num_rows; break; case BIT_PARAM: table_offset += (2 + 2 + index_sizes.string) * num_rows; break; case BIT_INTERFACEIMPL: row_count = max_rows( 3, yr_le32toh(rows.typedef_), yr_le32toh(rows.typeref), yr_le32toh(rows.typespec)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; table_offset += (index_sizes.typedef_ + index_size) * num_rows; break; case BIT_MEMBERREF: row_count = max_rows( 4, yr_le32toh(rows.methoddef), yr_le32toh(rows.moduleref), yr_le32toh(rows.typeref), yr_le32toh(rows.typespec)); if (row_count > (0xFFFF >> 0x03)) index_size = 4; else index_size = 2; row_size = (index_size + index_sizes.string + index_sizes.blob); memberref_row_size = row_size; memberref_ptr = table_offset; table_offset += row_size * num_rows; break; case BIT_CONSTANT: row_count = max_rows( 3, yr_le32toh(rows.param), yr_le32toh(rows.field), yr_le32toh(rows.property)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; // Using 'i' is insufficent since we may skip certain constants and // it would give an inaccurate count in that case. counter = 0; row_size = (1 + 1 + index_size + index_sizes.blob); row_ptr = table_offset; for (i = 0; i < num_rows; i++) { if (!fits_in_pe(pe, row_ptr, row_size)) break; constant_table = (PCONSTANT_TABLE) row_ptr; // Only look for constants of type string. if (yr_le32toh(constant_table->Type) != ELEMENT_TYPE_STRING) { row_ptr += row_size; continue; } // Get the blob offset and pull it out of the blob table. blob_offset = ((uint8_t*) constant_table) + 2 + index_size; if (index_sizes.blob == 4) blob_index = *(DWORD*) blob_offset; else // Cast the value (index into blob table) to a 32bit value. blob_index = (DWORD)(*(WORD*) blob_offset); // Everything checks out. Make sure the index into the blob field // is valid (non-null and within range). blob_offset = pe->data + metadata_root + yr_le32toh(streams->blob->Offset) + blob_index; blob_result = dotnet_parse_blob_entry(pe, blob_offset); if (blob_result.size == 0) { row_ptr += row_size; continue; } blob_length = blob_result.length; blob_offset += blob_result.size; // Quick sanity check to make sure the blob entry is within bounds. if (blob_offset + blob_length >= pe->data + pe->data_size) { row_ptr += row_size; continue; } set_sized_string( (char*) blob_offset, blob_result.length, pe->object, "constants[%i]", counter); counter++; row_ptr += row_size; } set_integer(counter, pe->object, "number_of_constants"); table_offset += row_size * num_rows; break; case BIT_CUSTOMATTRIBUTE: // index_size is size of the parent column. row_count = max_rows( 21, yr_le32toh(rows.methoddef), yr_le32toh(rows.field), yr_le32toh(rows.typeref), yr_le32toh(rows.typedef_), yr_le32toh(rows.param), yr_le32toh(rows.interfaceimpl), yr_le32toh(rows.memberref), yr_le32toh(rows.module), yr_le32toh(rows.property), yr_le32toh(rows.event), yr_le32toh(rows.standalonesig), yr_le32toh(rows.moduleref), yr_le32toh(rows.typespec), yr_le32toh(rows.assembly), yr_le32toh(rows.assemblyref), yr_le32toh(rows.file), yr_le32toh(rows.exportedtype), yr_le32toh(rows.manifestresource), yr_le32toh(rows.genericparam), yr_le32toh(rows.genericparamconstraint), yr_le32toh(rows.methodspec)); if (row_count > (0xFFFF >> 0x05)) index_size = 4; else index_size = 2; // index_size2 is size of the type column. row_count = max_rows( 2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref)); if (row_count > (0xFFFF >> 0x03)) index_size2 = 4; else index_size2 = 2; row_size = (index_size + index_size2 + index_sizes.blob); if (typeref_ptr != NULL && memberref_ptr != NULL) { row_ptr = table_offset; for (i = 0; i < num_rows; i++) { if (!fits_in_pe(pe, row_ptr, row_size)) break; // Check the Parent field. customattribute_table = (PCUSTOMATTRIBUTE_TABLE) row_ptr; if (index_size == 4) { // Low 5 bits tell us what this is an index into. Remaining bits // tell us the index value. // Parent must be an index into the Assembly (0x0E) table. if ((*(DWORD*) customattribute_table & 0x1F) != 0x0E) { row_ptr += row_size; continue; } } else { // Low 5 bits tell us what this is an index into. Remaining bits // tell us the index value. // Parent must be an index into the Assembly (0x0E) table. if ((*(WORD*) customattribute_table & 0x1F) != 0x0E) { row_ptr += row_size; continue; } } // Check the Type field. customattribute_table = (PCUSTOMATTRIBUTE_TABLE)( row_ptr + index_size); if (index_size2 == 4) { // Low 3 bits tell us what this is an index into. Remaining bits // tell us the index value. Only values 2 and 3 are defined. // Type must be an index into the MemberRef table. if ((*(DWORD*) customattribute_table & 0x07) != 0x03) { row_ptr += row_size; continue; } type_index = *(DWORD*) customattribute_table >> 3; } else { // Low 3 bits tell us what this is an index into. Remaining bits // tell us the index value. Only values 2 and 3 are defined. // Type must be an index into the MemberRef table. if ((*(WORD*) customattribute_table & 0x07) != 0x03) { row_ptr += row_size; continue; } // Cast the index to a 32bit value. type_index = (DWORD)((*(WORD*) customattribute_table >> 3)); } if (type_index > 0) type_index--; // Now follow the Type index into the MemberRef table. memberref_row = memberref_ptr + (memberref_row_size * type_index); if (!fits_in_pe(pe, memberref_row, memberref_row_size)) break; if (index_sizes.memberref == 4) { // Low 3 bits tell us what this is an index into. Remaining bits // tell us the index value. Class must be an index into the // TypeRef table. if ((*(DWORD*) memberref_row & 0x07) != 0x01) { row_ptr += row_size; continue; } class_index = *(DWORD*) memberref_row >> 3; } else { // Low 3 bits tell us what this is an index into. Remaining bits // tell us the index value. Class must be an index into the // TypeRef table. if ((*(WORD*) memberref_row & 0x07) != 0x01) { row_ptr += row_size; continue; } // Cast the index to a 32bit value. class_index = (DWORD)(*(WORD*) memberref_row >> 3); } if (class_index > 0) class_index--; // Now follow the Class index into the TypeRef table. typeref_row = typeref_ptr + (typeref_row_size * class_index); if (!fits_in_pe(pe, typeref_row, typeref_row_size)) break; // Skip over the ResolutionScope and check the Name field, // which is an index into the Strings heap. row_count = max_rows( 4, yr_le32toh(rows.module), yr_le32toh(rows.moduleref), yr_le32toh(rows.assemblyref), yr_le32toh(rows.typeref)); if (row_count > (0xFFFF >> 0x02)) typeref_row += 4; else typeref_row += 2; if (index_sizes.string == 4) { name = pe_get_dotnet_string( pe, string_offset, *(DWORD*) typeref_row); } else { name = pe_get_dotnet_string( pe, string_offset, *(WORD*) typeref_row); } if (name != NULL && strncmp(name, "GuidAttribute", 13) != 0) { row_ptr += row_size; continue; } // Get the Value field. customattribute_table = (PCUSTOMATTRIBUTE_TABLE)( row_ptr + index_size + index_size2); if (index_sizes.blob == 4) blob_index = *(DWORD*) customattribute_table; else // Cast the value (index into blob table) to a 32bit value. blob_index = (DWORD)(*(WORD*) customattribute_table); // Everything checks out. Make sure the index into the blob field // is valid (non-null and within range). blob_offset = pe->data + metadata_root + yr_le32toh(streams->blob->Offset) + blob_index; // If index into blob is 0 or past the end of the blob stream, skip // it. We don't know the size of the blob entry yet because that is // encoded in the start. if (blob_index == 0x00 || blob_offset >= pe->data + pe->data_size) { row_ptr += row_size; continue; } blob_result = dotnet_parse_blob_entry(pe, blob_offset); if (blob_result.size == 0) { row_ptr += row_size; continue; } blob_length = blob_result.length; blob_offset += blob_result.size; // Quick sanity check to make sure the blob entry is within bounds // and its length is at least 3 (2 bytes for the 16 bits prolog and // 1 byte for the string length) if (blob_length < 3 || blob_offset + blob_length >= pe->data + pe->data_size) { row_ptr += row_size; continue; } // Custom attributes MUST have a 16 bit prolog of 0x0001 if (*(WORD*) blob_offset != 0x0001) { row_ptr += row_size; continue; } // The next byte after the 16 bit prolog is the length of the string. blob_offset += 2; uint8_t str_len = *blob_offset; // Increment blob_offset so that it points to the first byte of the // string. blob_offset += 1; if (blob_offset + str_len > pe->data + pe->data_size) { row_ptr += row_size; continue; } if (*blob_offset == 0xFF || *blob_offset == 0x00) { typelib[0] = '\0'; } else { strncpy(typelib, (char*) blob_offset, str_len); typelib[str_len] = '\0'; } set_string(typelib, pe->object, "typelib"); row_ptr += row_size; } } table_offset += row_size * num_rows; break; case BIT_FIELDMARSHAL: row_count = max_rows(2, yr_le32toh(rows.field), yr_le32toh(rows.param)); if (row_count > (0xFFFF >> 0x01)) index_size = 4; else index_size = 2; table_offset += (index_size + index_sizes.blob) * num_rows; break; case BIT_DECLSECURITY: row_count = max_rows( 3, yr_le32toh(rows.typedef_), yr_le32toh(rows.methoddef), yr_le32toh(rows.assembly)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; table_offset += (2 + index_size + index_sizes.blob) * num_rows; break; case BIT_CLASSLAYOUT: table_offset += (2 + 4 + index_sizes.typedef_) * num_rows; break; case BIT_FIELDLAYOUT: table_offset += (4 + index_sizes.field) * num_rows; break; case BIT_STANDALONESIG: table_offset += (index_sizes.blob) * num_rows; break; case BIT_EVENTMAP: table_offset += (index_sizes.typedef_ + index_sizes.event) * num_rows; break; case BIT_EVENTPTR: // This one is not documented in ECMA-335. table_offset += (index_sizes.event) * num_rows; break; case BIT_EVENT: row_count = max_rows( 3, yr_le32toh(rows.typedef_), yr_le32toh(rows.typeref), yr_le32toh(rows.typespec)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; table_offset += (2 + index_sizes.string + index_size) * num_rows; break; case BIT_PROPERTYMAP: table_offset += (index_sizes.typedef_ + index_sizes.property) * num_rows; break; case BIT_PROPERTYPTR: // This one is not documented in ECMA-335. table_offset += (index_sizes.property) * num_rows; break; case BIT_PROPERTY: table_offset += (2 + index_sizes.string + index_sizes.blob) * num_rows; break; case BIT_METHODSEMANTICS: row_count = max_rows( 2, yr_le32toh(rows.event), yr_le32toh(rows.property)); if (row_count > (0xFFFF >> 0x01)) index_size = 4; else index_size = 2; table_offset += (2 + index_sizes.methoddef + index_size) * num_rows; break; case BIT_METHODIMPL: row_count = max_rows( 2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref)); if (row_count > (0xFFFF >> 0x01)) index_size = 4; else index_size = 2; table_offset += (index_sizes.typedef_ + (index_size * 2)) * num_rows; break; case BIT_MODULEREF: row_ptr = table_offset; // Can't use 'i' here because we only set the string if it is not // NULL. Instead use 'counter'. counter = 0; for (i = 0; i < num_rows; i++) { moduleref_table = (PMODULEREF_TABLE) row_ptr; if (!struct_fits_in_pe(pe, moduleref_table, MODULEREF_TABLE)) break; name = pe_get_dotnet_string( pe, string_offset, DOTNET_STRING_INDEX(moduleref_table->Name)); if (name != NULL) { set_string(name, pe->object, "modulerefs[%i]", counter); counter++; } row_ptr += index_sizes.string; } set_integer(counter, pe->object, "number_of_modulerefs"); table_offset += (index_sizes.string) * num_rows; break; case BIT_TYPESPEC: table_offset += (index_sizes.blob) * num_rows; break; case BIT_IMPLMAP: row_count = max_rows( 2, yr_le32toh(rows.field), yr_le32toh(rows.methoddef)); if (row_count > (0xFFFF >> 0x01)) index_size = 4; else index_size = 2; table_offset += (2 + index_size + index_sizes.string + index_sizes.moduleref) * num_rows; break; case BIT_FIELDRVA: row_size = 4 + index_sizes.field; row_ptr = table_offset; // Can't use 'i' here because we only set the field offset if it is // valid. Instead use 'counter'. counter = 0; for (i = 0; i < num_rows; i++) { fieldrva_table = (PFIELDRVA_TABLE) row_ptr; if (!struct_fits_in_pe(pe, fieldrva_table, FIELDRVA_TABLE)) break; field_offset = pe_rva_to_offset(pe, fieldrva_table->RVA); if (field_offset >= 0) { set_integer(field_offset, pe->object, "field_offsets[%i]", counter); counter++; } row_ptr += row_size; } set_integer(counter, pe->object, "number_of_field_offsets"); table_offset += row_size * num_rows; break; case BIT_ENCLOG: table_offset += (4 + 4) * num_rows; break; case BIT_ENCMAP: table_offset += (4) * num_rows; break; case BIT_ASSEMBLY: row_size = (4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob + (index_sizes.string * 2)); if (!fits_in_pe(pe, table_offset, row_size)) break; row_ptr = table_offset; assembly_table = (PASSEMBLY_TABLE) table_offset; set_integer( yr_le16toh(assembly_table->MajorVersion), pe->object, "assembly.version.major"); set_integer( yr_le16toh(assembly_table->MinorVersion), pe->object, "assembly.version.minor"); set_integer( yr_le16toh(assembly_table->BuildNumber), pe->object, "assembly.version.build_number"); set_integer( yr_le16toh(assembly_table->RevisionNumber), pe->object, "assembly.version.revision_number"); // Can't use assembly_table here because the PublicKey comes before // Name and is a variable length field. if (index_sizes.string == 4) name = pe_get_dotnet_string( pe, string_offset, yr_le32toh(*( DWORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob))); else name = pe_get_dotnet_string( pe, string_offset, yr_le16toh( *(WORD*) (row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob))); if (name != NULL) set_string(name, pe->object, "assembly.name"); // Culture comes after Name. if (index_sizes.string == 4) { name = pe_get_dotnet_string( pe, string_offset, yr_le32toh(*(DWORD*) ( row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob + index_sizes.string))); } else { name = pe_get_dotnet_string( pe, string_offset, yr_le16toh(*(WORD*) ( row_ptr + 4 + 2 + 2 + 2 + 2 + 4 + index_sizes.blob + index_sizes.string))); } // Sometimes it will be a zero length string. This is technically // against the specification but happens from time to time. if (name != NULL && strlen(name) > 0) set_string(name, pe->object, "assembly.culture"); table_offset += row_size * num_rows; break; case BIT_ASSEMBLYPROCESSOR: table_offset += (4) * num_rows; break; case BIT_ASSEMBLYOS: table_offset += (4 + 4 + 4) * num_rows; break; case BIT_ASSEMBLYREF: row_size = (2 + 2 + 2 + 2 + 4 + (index_sizes.blob * 2) + (index_sizes.string * 2)); row_ptr = table_offset; for (i = 0; i < num_rows; i++) { if (!fits_in_pe(pe, row_ptr, row_size)) break; assemblyref_table = (PASSEMBLYREF_TABLE) row_ptr; set_integer( yr_le16toh(assemblyref_table->MajorVersion), pe->object, "assembly_refs[%i].version.major", i); set_integer( yr_le16toh(assemblyref_table->MinorVersion), pe->object, "assembly_refs[%i].version.minor", i); set_integer( yr_le16toh(assemblyref_table->BuildNumber), pe->object, "assembly_refs[%i].version.build_number", i); set_integer( yr_le16toh(assemblyref_table->RevisionNumber), pe->object, "assembly_refs[%i].version.revision_number", i); blob_offset = pe->data + metadata_root + yr_le32toh(streams->blob->Offset); if (index_sizes.blob == 4) blob_offset += yr_le32toh( assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Long); else blob_offset += yr_le16toh( assemblyref_table->PublicKeyOrToken.PublicKeyOrToken_Short); blob_result = dotnet_parse_blob_entry(pe, blob_offset); blob_offset += blob_result.size; if (blob_result.size == 0 || !fits_in_pe(pe, blob_offset, blob_result.length)) { row_ptr += row_size; continue; } // Avoid empty strings. if (blob_result.length > 0) { set_sized_string( (char*) blob_offset, blob_result.length, pe->object, "assembly_refs[%i].public_key_or_token", i); } // Can't use assemblyref_table here because the PublicKey comes before // Name and is a variable length field. if (index_sizes.string == 4) name = pe_get_dotnet_string( pe, string_offset, yr_le32toh( *(DWORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob))); else name = pe_get_dotnet_string( pe, string_offset, yr_le16toh( *(WORD*) (row_ptr + 2 + 2 + 2 + 2 + 4 + index_sizes.blob))); if (name != NULL) set_string(name, pe->object, "assembly_refs[%i].name", i); row_ptr += row_size; } set_integer(i, pe->object, "number_of_assembly_refs"); table_offset += row_size * num_rows; break; case BIT_ASSEMBLYREFPROCESSOR: table_offset += (4 + index_sizes.assemblyrefprocessor) * num_rows; break; case BIT_ASSEMBLYREFOS: table_offset += (4 + 4 + 4 + index_sizes.assemblyref) * num_rows; break; case BIT_FILE: table_offset += (4 + index_sizes.string + index_sizes.blob) * num_rows; break; case BIT_EXPORTEDTYPE: row_count = max_rows( 3, yr_le32toh(rows.file), yr_le32toh(rows.assemblyref), yr_le32toh(rows.exportedtype)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; table_offset += (4 + 4 + (index_sizes.string * 2) + index_size) * num_rows; break; case BIT_MANIFESTRESOURCE: // This is an Implementation coded index with no 3rd bit specified. row_count = max_rows( 2, yr_le32toh(rows.file), yr_le32toh(rows.assemblyref)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; row_size = (4 + 4 + index_sizes.string + index_size); // Using 'i' is insufficent since we may skip certain resources and // it would give an inaccurate count in that case. counter = 0; row_ptr = table_offset; // First DWORD is the offset. for (i = 0; i < num_rows; i++) { if (!fits_in_pe(pe, row_ptr, row_size)) break; manifestresource_table = (PMANIFESTRESOURCE_TABLE) row_ptr; resource_offset = yr_le32toh(manifestresource_table->Offset); // Only set offset if it is in this file (implementation != 0). // Can't use manifestresource_table here because the Name and // Implementation fields are variable size. if (index_size == 4) implementation = yr_le32toh( *(DWORD*) (row_ptr + 4 + 4 + index_sizes.string)); else implementation = yr_le16toh( *(WORD*) (row_ptr + 4 + 4 + index_sizes.string)); if (implementation != 0) { row_ptr += row_size; continue; } if (!fits_in_pe( pe, pe->data + resource_base + resource_offset, sizeof(DWORD))) { row_ptr += row_size; continue; } resource_size = yr_le32toh( *(DWORD*) (pe->data + resource_base + resource_offset)); if (!fits_in_pe( pe, pe->data + resource_base + resource_offset, resource_size)) { row_ptr += row_size; continue; } // Add 4 to skip the size. set_integer( resource_base + resource_offset + 4, pe->object, "resources[%i].offset", counter); set_integer(resource_size, pe->object, "resources[%i].length", counter); name = pe_get_dotnet_string( pe, string_offset, DOTNET_STRING_INDEX(manifestresource_table->Name)); if (name != NULL) set_string(name, pe->object, "resources[%i].name", counter); row_ptr += row_size; counter++; } set_integer(counter, pe->object, "number_of_resources"); table_offset += row_size * num_rows; break; case BIT_NESTEDCLASS: table_offset += (index_sizes.typedef_ * 2) * num_rows; break; case BIT_GENERICPARAM: row_count = max_rows( 2, yr_le32toh(rows.typedef_), yr_le32toh(rows.methoddef)); if (row_count > (0xFFFF >> 0x01)) index_size = 4; else index_size = 2; table_offset += (2 + 2 + index_size + index_sizes.string) * num_rows; break; case BIT_METHODSPEC: row_count = max_rows( 2, yr_le32toh(rows.methoddef), yr_le32toh(rows.memberref)); if (row_count > (0xFFFF >> 0x01)) index_size = 4; else index_size = 2; table_offset += (index_size + index_sizes.blob) * num_rows; break; case BIT_GENERICPARAMCONSTRAINT: row_count = max_rows( 3, yr_le32toh(rows.typedef_), yr_le32toh(rows.typeref), yr_le32toh(rows.typespec)); if (row_count > (0xFFFF >> 0x02)) index_size = 4; else index_size = 2; table_offset += (index_sizes.genericparam + index_size) * num_rows; break; default: // printf("Unknown bit: %i\n", bit_check); return; } matched_bits++; } } // Parsing the #~ stream is done in two parts. The first part (this function) // parses enough of the Stream to provide context for the second pass. In // particular it is collecting the number of rows for each of the tables. The // second part parses the actual tables of interest. void dotnet_parse_tilde( PE* pe, int64_t metadata_root, PCLI_HEADER cli_header, PSTREAMS streams) { PTILDE_HEADER tilde_header; int64_t resource_base; uint32_t* row_offset = NULL; int bit_check; // This is used as an offset into the rows and tables. For every bit set in // Valid this will be incremented. This is because the bit position doesn't // matter, just the number of bits that are set, when determining how many // rows and what the table structure is. int matched_bits = 0; // We need to know the number of rows for some tables, because they are // indexed into. The index will be either 2 or 4 bytes, depending upon the // number of rows being indexed into. ROWS rows; INDEX_SIZES index_sizes; uint32_t heap_sizes; // Default all rows to 0. They will be set to actual values later on, if // they exist in the file. memset(&rows, '\0', sizeof(ROWS)); // Default index sizes are 2. Will be bumped to 4 if necessary. memset(&index_sizes, 2, sizeof(index_sizes)); tilde_header = (PTILDE_HEADER)( pe->data + metadata_root + yr_le32toh(streams->tilde->Offset)); if (!struct_fits_in_pe(pe, tilde_header, TILDE_HEADER)) return; heap_sizes = yr_le32toh(tilde_header->HeapSizes); // Set index sizes for various heaps. if (heap_sizes & 0x01) index_sizes.string = 4; if (heap_sizes & 0x02) index_sizes.guid = 4; if (heap_sizes & 0x04) index_sizes.blob = 4; // Immediately after the tilde header is an array of 32bit values which // indicate how many rows are in each table. The tables are immediately // after the rows array. // // Save the row offset. row_offset = (uint32_t*) (tilde_header + 1); // Walk all the bits first because we need to know the number of rows for // some tables in order to parse others. In particular this applies to // coded indexes, which are documented in ECMA-335 II.24.2.6. for (bit_check = 0; bit_check < 64; bit_check++) { if (!((yr_le64toh(tilde_header->Valid) >> bit_check) & 0x01)) continue; #define ROW_CHECK(name) \ if (fits_in_pe(pe, row_offset, (matched_bits + 1) * sizeof(uint32_t))) \ rows.name = *(row_offset + matched_bits); #define ROW_CHECK_WITH_INDEX(name) \ ROW_CHECK(name); \ if (yr_le32toh(rows.name) > 0xFFFF) \ index_sizes.name = 4; switch (bit_check) { case BIT_MODULE: ROW_CHECK(module); break; case BIT_MODULEREF: ROW_CHECK_WITH_INDEX(moduleref); break; case BIT_ASSEMBLYREF: ROW_CHECK_WITH_INDEX(assemblyref); break; case BIT_ASSEMBLYREFPROCESSOR: ROW_CHECK_WITH_INDEX(assemblyrefprocessor); break; case BIT_TYPEREF: ROW_CHECK(typeref); break; case BIT_METHODDEF: ROW_CHECK_WITH_INDEX(methoddef); break; case BIT_MEMBERREF: ROW_CHECK_WITH_INDEX(memberref); break; case BIT_TYPEDEF: ROW_CHECK_WITH_INDEX(typedef_); break; case BIT_TYPESPEC: ROW_CHECK(typespec); break; case BIT_FIELD: ROW_CHECK_WITH_INDEX(field); break; case BIT_PARAM: ROW_CHECK_WITH_INDEX(param); break; case BIT_PROPERTY: ROW_CHECK_WITH_INDEX(property); break; case BIT_INTERFACEIMPL: ROW_CHECK(interfaceimpl); break; case BIT_EVENT: ROW_CHECK_WITH_INDEX(event); break; case BIT_STANDALONESIG: ROW_CHECK(standalonesig); break; case BIT_ASSEMBLY: ROW_CHECK(assembly); break; case BIT_FILE: ROW_CHECK(file); break; case BIT_EXPORTEDTYPE: ROW_CHECK(exportedtype); break; case BIT_MANIFESTRESOURCE: ROW_CHECK(manifestresource); break; case BIT_GENERICPARAM: ROW_CHECK_WITH_INDEX(genericparam); break; case BIT_GENERICPARAMCONSTRAINT: ROW_CHECK(genericparamconstraint); break; case BIT_METHODSPEC: ROW_CHECK(methodspec); break; default: break; } matched_bits++; } // This is used when parsing the MANIFEST RESOURCE table. resource_base = pe_rva_to_offset( pe, yr_le32toh(cli_header->Resources.VirtualAddress)); dotnet_parse_tilde_2( pe, tilde_header, resource_base, metadata_root, rows, index_sizes, streams); } void dotnet_parse_com(PE* pe) { PIMAGE_DATA_DIRECTORY directory; PCLI_HEADER cli_header; PNET_METADATA metadata; int64_t metadata_root, offset; char* end; STREAMS headers; WORD num_streams; uint32_t md_len; directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR); if (directory == NULL) return; offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress)); if (offset < 0 || !struct_fits_in_pe(pe, pe->data + offset, CLI_HEADER)) return; cli_header = (PCLI_HEADER)(pe->data + offset); offset = metadata_root = pe_rva_to_offset( pe, yr_le32toh(cli_header->MetaData.VirtualAddress)); if (!struct_fits_in_pe(pe, pe->data + offset, NET_METADATA)) return; metadata = (PNET_METADATA)(pe->data + offset); if (yr_le32toh(metadata->Magic) != NET_METADATA_MAGIC) return; // Version length must be between 1 and 255, and be a multiple of 4. // Also make sure it fits in pe. md_len = yr_le32toh(metadata->Length); if (md_len == 0 || md_len > 255 || md_len % 4 != 0 || !fits_in_pe(pe, pe->data + offset, md_len)) { return; } // The length includes the NULL terminator and is rounded up to a multiple of // 4. We need to exclude the terminator and the padding, so search for the // first NULL byte. end = (char*) memmem((void*) metadata->Version, md_len, "\0", 1); if (end != NULL) set_sized_string( metadata->Version, (end - metadata->Version), pe->object, "version"); // The metadata structure has some variable length records after the version. // We must manually parse things from here on out. // // Flags are 2 bytes (always 0). offset += sizeof(NET_METADATA) + md_len + 2; // 2 bytes for Streams. if (!fits_in_pe(pe, pe->data + offset, 2)) return; num_streams = (WORD) * (pe->data + offset); offset += 2; headers = dotnet_parse_stream_headers(pe, offset, metadata_root, num_streams); if (headers.guid != NULL) dotnet_parse_guid(pe, metadata_root, headers.guid); // Parse the #~ stream, which includes various tables of interest. // These tables reference the blob and string streams, so we need to ensure // those are not NULL also. if (headers.tilde != NULL && headers.string != NULL && headers.blob != NULL) dotnet_parse_tilde(pe, metadata_root, cli_header, &headers); if (headers.us != NULL) dotnet_parse_us(pe, metadata_root, headers.us); } begin_declarations declare_string("version"); declare_string("module_name"); begin_struct_array("streams") declare_string("name"); declare_integer("offset"); declare_integer("size"); end_struct_array("streams") declare_integer("number_of_streams"); declare_string_array("guids"); declare_integer("number_of_guids"); begin_struct_array("resources") declare_integer("offset"); declare_integer("length"); declare_string("name"); end_struct_array("resources") declare_integer("number_of_resources"); begin_struct_array("assembly_refs") begin_struct("version") declare_integer("major"); declare_integer("minor"); declare_integer("build_number"); declare_integer("revision_number"); end_struct("version") declare_string("public_key_or_token"); declare_string("name"); end_struct_array("assembly_refs") declare_integer("number_of_assembly_refs"); begin_struct("assembly") begin_struct("version") declare_integer("major"); declare_integer("minor"); declare_integer("build_number"); declare_integer("revision_number"); end_struct("version") declare_string("name"); declare_string("culture"); end_struct("assembly") declare_string_array("modulerefs"); declare_integer("number_of_modulerefs"); declare_string_array("user_strings"); declare_integer("number_of_user_strings"); declare_string("typelib"); declare_string_array("constants"); declare_integer("number_of_constants"); declare_integer_array("field_offsets"); declare_integer("number_of_field_offsets"); end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; const uint8_t* block_data = NULL; foreach_memory_block(iterator, block) { PIMAGE_NT_HEADERS32 pe_header; block_data = block->fetch_data(block); if (block_data == NULL) continue; pe_header = pe_get_header(block_data, block->size); if (pe_header != NULL) { // Ignore DLLs while scanning a process if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) || !(pe_header->FileHeader.Characteristics & IMAGE_FILE_DLL)) { PE* pe = (PE*) yr_malloc(sizeof(PE)); if (pe == NULL) return ERROR_INSUFFICIENT_MEMORY; pe->data = block_data; pe->data_size = block->size; pe->object = module_object; pe->header = pe_header; module_object->data = pe; dotnet_parse_com(pe); break; } } } return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { PE* pe = (PE*) module_object->data; if (pe == NULL) return ERROR_SUCCESS; yr_free(pe); return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/elf/000077500000000000000000000000001413423160300163115ustar00rootroot00000000000000yara-4.1.3/libyara/modules/elf/elf.c000066400000000000000000001365541413423160300172410ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #define MODULE_NAME elf #define CLASS_DATA(c, d) ((c << 8) | d) int get_elf_class_data(const uint8_t* buffer, size_t buffer_length) { elf_ident_t* elf_ident; if (buffer_length < sizeof(elf_ident_t)) return 0; elf_ident = (elf_ident_t*) buffer; if (yr_le32toh(elf_ident->magic) == ELF_MAGIC) { return CLASS_DATA(elf_ident->_class, elf_ident->data); } else { return 0; } } static bool is_valid_ptr( const void* base, size_t size, const void* ptr, uint64_t ptr_size) // ptr_size can be 64bit even in 32bit systems. { return ptr >= base && ptr_size <= size && ((char*) ptr) + ptr_size <= ((char*) base) + size; } #define IS_VALID_PTR(base, size, ptr) \ is_valid_ptr(base, size, ptr, sizeof(*ptr)) // // Returns a string table entry for the index or NULL if the entry is out // of bounds. A non-null return value will be a null-terminated C string. // static const char* str_table_entry( const char* str_table_base, const char* str_table_limit, int index) { size_t len; const char* str_entry; if (str_table_base >= str_table_limit) return NULL; // The first entry in the string table must be a null character, if not the // string table is probably corrupted. if (*str_table_base != '\0') return NULL; if (index < 0) return NULL; str_entry = str_table_base + index; if (str_entry >= str_table_limit) return NULL; len = strnlen(str_entry, str_table_limit - str_entry); // Entry is clamped by extent of string table, not null-terminated. if (str_entry + len == str_table_limit) return NULL; return str_entry; } #define ELF_SIZE_OF_SECTION_TABLE(bits, bo, h) \ (sizeof(elf##bits##_section_header_t) * yr_##bo##16toh(h->sh_entry_count)) #define ELF_SIZE_OF_PROGRAM_TABLE(bits, bo, h) \ (sizeof(elf##bits##_program_header_t) * yr_##bo##16toh(h->ph_entry_count)) #define ELF_RVA_TO_OFFSET(bits, bo) \ uint64_t elf_rva_to_offset_##bits##_##bo( \ elf##bits##_header_t* elf_header, uint64_t rva, size_t elf_size) \ { \ if (yr_##bo##16toh(elf_header->type) == ELF_ET_EXEC) \ { \ int i; \ \ elf##bits##_program_header_t* program; \ \ /* check that ph_offset doesn't wrap when added to SIZE_OF_PROGRAM_TABLE \ */ \ \ if (ULONG_MAX - yr_##bo##bits##toh(elf_header->ph_offset) < \ ELF_SIZE_OF_PROGRAM_TABLE(bits, bo, elf_header)) \ { \ return YR_UNDEFINED; \ } \ \ if (yr_##bo##bits##toh(elf_header->ph_offset) == 0 || \ yr_##bo##bits##toh(elf_header->ph_offset) > elf_size || \ yr_##bo##bits##toh(elf_header->ph_offset) + \ ELF_SIZE_OF_PROGRAM_TABLE(bits, bo, elf_header) > \ elf_size || \ yr_##bo##16toh(elf_header->ph_entry_count) == 0) \ { \ return YR_UNDEFINED; \ } \ \ program = (elf##bits##_program_header_t*) \ ((uint8_t*) elf_header + yr_##bo##bits##toh(elf_header->ph_offset)); \ \ for (i = 0; i < yr_##bo##16toh(elf_header->ph_entry_count); i++) \ { \ if (rva >= yr_##bo##bits##toh(program->virt_addr) && \ rva < yr_##bo##bits##toh(program->virt_addr) + \ yr_##bo##bits##toh(program->mem_size)) \ { \ return yr_##bo##bits##toh(program->offset) + \ (rva - yr_##bo##bits##toh(program->virt_addr)); \ } \ \ program++; \ } \ } \ else \ { \ int i; \ \ elf##bits##_section_header_t* section; \ \ /* check that sh_offset doesn't wrap when added to SIZE_OF_SECTION_TABLE \ */ \ \ if (ULONG_MAX - yr_##bo##bits##toh(elf_header->sh_offset) < \ ELF_SIZE_OF_SECTION_TABLE(bits, bo, elf_header)) \ { \ return YR_UNDEFINED; \ } \ \ if (yr_##bo##bits##toh(elf_header->sh_offset) == 0 || \ yr_##bo##bits##toh(elf_header->sh_offset) > elf_size || \ yr_##bo##bits##toh(elf_header->sh_offset) + \ ELF_SIZE_OF_SECTION_TABLE(bits, bo, elf_header) > \ elf_size || \ yr_##bo##16toh(elf_header->sh_entry_count) == 0) \ { \ return YR_UNDEFINED; \ } \ \ section = (elf##bits##_section_header_t*) \ ((uint8_t*) elf_header + yr_##bo##bits##toh(elf_header->sh_offset)); \ \ for (i = 0; i < yr_##bo##16toh(elf_header->sh_entry_count); i++) \ { \ if (yr_##bo##32toh(section->type) != ELF_SHT_NULL && \ yr_##bo##32toh(section->type) != ELF_SHT_NOBITS && \ rva >= yr_##bo##bits##toh(section->addr) && \ rva < yr_##bo##bits##toh(section->addr) + \ yr_##bo##bits##toh(section->size)) \ { \ return yr_##bo##bits##toh(section->offset) + \ (rva - yr_##bo##bits##toh(section->addr)); \ } \ \ section++; \ } \ } \ return YR_UNDEFINED; \ } #define PARSE_ELF_HEADER(bits, bo) \ void parse_elf_header_##bits##_##bo( \ elf##bits##_header_t* elf, \ uint64_t base_address, \ size_t elf_size, \ int flags, \ YR_OBJECT* elf_obj) \ { \ unsigned int i, j, m; \ const char* elf_raw = (const char*) elf; \ uint16_t str_table_index = yr_##bo##16toh(elf->sh_str_table_index); \ \ const char* sym_table = NULL; \ const char* sym_str_table = NULL; \ const char* dyn_sym_table = NULL; \ const char* dyn_sym_str_table = NULL; \ \ uint##bits##_t sym_table_size = 0; \ uint##bits##_t sym_str_table_size = 0; \ uint##bits##_t dyn_sym_table_size = 0; \ uint##bits##_t dyn_sym_str_table_size = 0; \ \ elf##bits##_section_header_t* section_table; \ elf##bits##_section_header_t* section; \ elf##bits##_program_header_t* segment; \ \ set_integer(yr_##bo##16toh(elf->type), elf_obj, "type"); \ set_integer(yr_##bo##16toh(elf->machine), elf_obj, "machine"); \ set_integer(yr_##bo##bits##toh(elf->sh_offset), elf_obj, "sh_offset"); \ set_integer(yr_##bo##16toh(elf->sh_entry_size), elf_obj, "sh_entry_size"); \ set_integer( \ yr_##bo##16toh(elf->sh_entry_count), elf_obj, "number_of_sections"); \ set_integer(yr_##bo##bits##toh(elf->ph_offset), elf_obj, "ph_offset"); \ set_integer(yr_##bo##16toh(elf->ph_entry_size), elf_obj, "ph_entry_size"); \ set_integer( \ yr_##bo##16toh(elf->ph_entry_count), elf_obj, "number_of_segments"); \ \ if (yr_##bo##bits##toh(elf->entry) != 0) \ { \ set_integer( \ flags& SCAN_FLAGS_PROCESS_MEMORY \ ? base_address + yr_##bo##bits##toh(elf->entry) \ : elf_rva_to_offset_##bits##_##bo( \ elf, yr_##bo##bits##toh(elf->entry), elf_size), \ elf_obj, \ "entry_point"); \ } \ \ if (yr_##bo##16toh(elf->sh_entry_count) < ELF_SHN_LORESERVE && \ str_table_index < yr_##bo##16toh(elf->sh_entry_count) && \ yr_##bo##bits##toh(elf->sh_offset) < elf_size && \ yr_##bo##bits##toh(elf->sh_offset) + \ yr_##bo##16toh(elf->sh_entry_count) * \ sizeof(elf##bits##_section_header_t) <= \ elf_size) \ { \ const char* str_table = NULL; \ \ section_table = \ (elf##bits##_section_header_t*) (elf_raw + yr_##bo##bits##toh(elf->sh_offset)); \ \ if (yr_##bo##bits##toh(section_table[str_table_index].offset) < \ elf_size) \ { \ str_table = elf_raw + \ yr_##bo##bits##toh(section_table[str_table_index].offset); \ } \ \ section = section_table; \ \ for (i = 0; i < yr_##bo##16toh(elf->sh_entry_count); i++, section++) \ { \ set_integer( \ yr_##bo##32toh(section->type), elf_obj, "sections[%i].type", i); \ set_integer( \ yr_##bo##bits##toh(section->flags), \ elf_obj, \ "sections[%i].flags", \ i); \ set_integer( \ yr_##bo##bits##toh(section->addr), \ elf_obj, \ "sections[%i].address", \ i); \ set_integer( \ yr_##bo##bits##toh(section->size), \ elf_obj, \ "sections[%i].size", \ i); \ set_integer( \ yr_##bo##bits##toh(section->offset), \ elf_obj, \ "sections[%i].offset", \ i); \ \ if (yr_##bo##32toh(section->name) < elf_size && str_table > elf_raw) \ { \ const char* section_name = str_table_entry( \ str_table, elf_raw + elf_size, yr_##bo##32toh(section->name)); \ \ if (section_name) \ set_string(section_name, elf_obj, "sections[%i].name", i); \ } \ \ if (yr_##bo##32toh(section->type) == ELF_SHT_SYMTAB && \ yr_##bo##32toh(section->link) < elf->sh_entry_count) \ { \ elf##bits##_section_header_t* string_section = section_table + \ yr_##bo##32toh( \ section->link); \ \ if (IS_VALID_PTR(elf, elf_size, string_section) && \ yr_##bo##32toh(string_section->type) == ELF_SHT_STRTAB) \ { \ sym_table = elf_raw + yr_##bo##bits##toh(section->offset); \ sym_str_table = elf_raw + \ yr_##bo##bits##toh(string_section->offset); \ sym_table_size = yr_##bo##bits##toh(section->size); \ sym_str_table_size = yr_##bo##bits##toh(string_section->size); \ } \ } \ \ if (yr_##bo##32toh(section->type) == ELF_SHT_DYNSYM && \ yr_##bo##32toh(section->link) < elf->sh_entry_count) \ { \ elf##bits##_section_header_t* dynstr_section = section_table + \ yr_##bo##32toh( \ section->link); \ \ if (IS_VALID_PTR(elf, elf_size, dynstr_section) && \ yr_##bo##32toh(dynstr_section->type) == ELF_SHT_STRTAB) \ { \ dyn_sym_table = elf_raw + yr_##bo##bits##toh(section->offset); \ dyn_sym_str_table = elf_raw + \ yr_##bo##bits##toh(dynstr_section->offset); \ dyn_sym_table_size = yr_##bo##bits##toh(section->size); \ dyn_sym_str_table_size = yr_##bo##bits##toh(dynstr_section->size); \ } \ } \ } \ \ if (is_valid_ptr(elf, elf_size, sym_str_table, sym_str_table_size) && \ is_valid_ptr(elf, elf_size, sym_table, sym_table_size)) \ { \ elf##bits##_sym_t* sym = (elf##bits##_sym_t*) sym_table; \ \ for (j = 0; j < sym_table_size / sizeof(elf##bits##_sym_t); \ j++, sym++) \ { \ const char* sym_name = str_table_entry( \ sym_str_table, \ sym_str_table + sym_str_table_size, \ yr_##bo##32toh(sym->name)); \ \ if (sym_name) \ set_string(sym_name, elf_obj, "symtab[%i].name", j); \ \ set_integer(sym->info >> 4, elf_obj, "symtab[%i].bind", j); \ set_integer(sym->info & 0xf, elf_obj, "symtab[%i].type", j); \ set_integer( \ yr_##bo##16toh(sym->shndx), elf_obj, "symtab[%i].shndx", j); \ set_integer( \ yr_##bo##bits##toh(sym->value), elf_obj, "symtab[%i].value", j); \ set_integer( \ yr_##bo##bits##toh(sym->size), elf_obj, "symtab[%i].size", j); \ } \ \ set_integer(j, elf_obj, "symtab_entries"); \ } \ \ if (is_valid_ptr(elf, elf_size, dyn_sym_str_table, dyn_sym_str_table_size) && \ is_valid_ptr(elf, elf_size, dyn_sym_table, dyn_sym_table_size)) \ { \ elf##bits##_sym_t* dynsym = (elf##bits##_sym_t*) dyn_sym_table; \ \ for (m = 0; m < dyn_sym_table_size / sizeof(elf##bits##_sym_t); \ m++, dynsym++) \ { \ const char* dynsym_name = str_table_entry( \ dyn_sym_str_table, \ dyn_sym_str_table + dyn_sym_str_table_size, \ yr_##bo##32toh(dynsym->name)); \ \ if (dynsym_name) \ set_string(dynsym_name, elf_obj, "dynsym[%i].name", m); \ \ set_integer(dynsym->info >> 4, elf_obj, "dynsym[%i].bind", m); \ set_integer(dynsym->info & 0xf, elf_obj, "dynsym[%i].type", m); \ set_integer( \ yr_##bo##16toh(dynsym->shndx), elf_obj, "dynsym[%i].shndx", m); \ set_integer( \ yr_##bo##bits##toh(dynsym->value), elf_obj, "dynsym[%i].value", m); \ set_integer( \ yr_##bo##bits##toh(dynsym->size), elf_obj, "dynsym[%i].size", m); \ } \ \ set_integer(m, elf_obj, "dynsym_entries"); \ } \ } \ \ if (yr_##bo##16toh(elf->ph_entry_count) > 0 && \ yr_##bo##16toh(elf->ph_entry_count) < ELF_PN_XNUM && \ yr_##bo##bits##toh(elf->ph_offset) < elf_size && \ yr_##bo##bits##toh(elf->ph_offset) + \ yr_##bo##16toh(elf->ph_entry_count) * \ sizeof(elf##bits##_program_header_t) <= \ elf_size) \ { \ segment = \ (elf##bits##_program_header_t*) (elf_raw + yr_##bo##bits##toh(elf->ph_offset)); \ \ for (i = 0; i < yr_##bo##16toh(elf->ph_entry_count); i++, segment++) \ { \ set_integer( \ yr_##bo##32toh(segment->type), elf_obj, "segments[%i].type", i); \ set_integer( \ yr_##bo##32toh(segment->flags), elf_obj, "segments[%i].flags", i); \ set_integer( \ yr_##bo##bits##toh(segment->offset), \ elf_obj, \ "segments[%i].offset", \ i); \ set_integer( \ yr_##bo##bits##toh(segment->virt_addr), \ elf_obj, \ "segments[%i].virtual_address", \ i); \ set_integer( \ yr_##bo##bits##toh(segment->phys_addr), \ elf_obj, \ "segments[%i].physical_address", \ i); \ set_integer( \ yr_##bo##bits##toh(segment->file_size), \ elf_obj, \ "segments[%i].file_size", \ i); \ set_integer( \ yr_##bo##bits##toh(segment->mem_size), \ elf_obj, \ "segments[%i].memory_size", \ i); \ set_integer( \ yr_##bo##bits##toh(segment->alignment), \ elf_obj, \ "segments[%i].alignment", \ i); \ \ if (yr_##bo##32toh(segment->type) == ELF_PT_DYNAMIC) \ { \ elf##bits##_dyn_t* dyn = \ (elf##bits##_dyn_t*) (elf_raw + yr_##bo##bits##toh(segment->offset)); \ \ for (j = 0; IS_VALID_PTR(elf, elf_size, dyn); dyn++, j++) \ { \ set_integer( \ yr_##bo##bits##toh(dyn->tag), elf_obj, "dynamic[%i].type", j); \ set_integer( \ yr_##bo##bits##toh(dyn->val), elf_obj, "dynamic[%i].val", j); \ \ if (dyn->tag == ELF_DT_NULL) \ { \ j++; \ break; \ } \ } \ set_integer(j, elf_obj, "dynamic_section_entries"); \ } \ } \ } \ } ELF_RVA_TO_OFFSET(32, le); ELF_RVA_TO_OFFSET(64, le); ELF_RVA_TO_OFFSET(32, be); ELF_RVA_TO_OFFSET(64, be); PARSE_ELF_HEADER(32, le); PARSE_ELF_HEADER(64, le); PARSE_ELF_HEADER(32, be); PARSE_ELF_HEADER(64, be); begin_declarations declare_integer("ET_NONE"); declare_integer("ET_REL"); declare_integer("ET_EXEC"); declare_integer("ET_DYN"); declare_integer("ET_CORE"); declare_integer("EM_NONE"); declare_integer("EM_M32"); declare_integer("EM_SPARC"); declare_integer("EM_386"); declare_integer("EM_68K"); declare_integer("EM_88K"); declare_integer("EM_860"); declare_integer("EM_MIPS"); declare_integer("EM_MIPS_RS3_LE"); declare_integer("EM_PPC"); declare_integer("EM_PPC64"); declare_integer("EM_ARM"); declare_integer("EM_X86_64"); declare_integer("EM_AARCH64"); declare_integer("SHT_NULL"); declare_integer("SHT_PROGBITS"); declare_integer("SHT_SYMTAB"); declare_integer("SHT_STRTAB"); declare_integer("SHT_RELA"); declare_integer("SHT_HASH"); declare_integer("SHT_DYNAMIC"); declare_integer("SHT_NOTE"); declare_integer("SHT_NOBITS"); declare_integer("SHT_REL"); declare_integer("SHT_SHLIB"); declare_integer("SHT_DYNSYM"); declare_integer("SHF_WRITE"); declare_integer("SHF_ALLOC"); declare_integer("SHF_EXECINSTR"); declare_integer("type"); declare_integer("machine"); declare_integer("entry_point"); declare_integer("number_of_sections"); declare_integer("sh_offset"); declare_integer("sh_entry_size"); declare_integer("number_of_segments"); declare_integer("ph_offset"); declare_integer("ph_entry_size"); begin_struct_array("sections") declare_integer("type"); declare_integer("flags"); declare_integer("address"); declare_string("name"); declare_integer("size"); declare_integer("offset"); end_struct_array("sections") declare_integer("PT_NULL"); declare_integer("PT_LOAD"); declare_integer("PT_DYNAMIC"); declare_integer("PT_INTERP"); declare_integer("PT_NOTE"); declare_integer("PT_SHLIB"); declare_integer("PT_PHDR"); declare_integer("PT_TLS"); declare_integer("PT_GNU_EH_FRAME"); declare_integer("PT_GNU_STACK"); declare_integer("DT_NULL"); declare_integer("DT_NEEDED"); declare_integer("DT_PLTRELSZ"); declare_integer("DT_PLTGOT"); declare_integer("DT_HASH"); declare_integer("DT_STRTAB"); declare_integer("DT_SYMTAB"); declare_integer("DT_RELA"); declare_integer("DT_RELASZ"); declare_integer("DT_RELAENT"); declare_integer("DT_STRSZ"); declare_integer("DT_SYMENT"); declare_integer("DT_INIT"); declare_integer("DT_FINI"); declare_integer("DT_SONAME"); declare_integer("DT_RPATH"); declare_integer("DT_SYMBOLIC"); declare_integer("DT_REL"); declare_integer("DT_RELSZ"); declare_integer("DT_RELENT"); declare_integer("DT_PLTREL"); declare_integer("DT_DEBUG"); declare_integer("DT_TEXTREL"); declare_integer("DT_JMPREL"); declare_integer("DT_BIND_NOW"); declare_integer("DT_INIT_ARRAY"); declare_integer("DT_FINI_ARRAY"); declare_integer("DT_INIT_ARRAYSZ"); declare_integer("DT_FINI_ARRAYSZ"); declare_integer("DT_RUNPATH"); declare_integer("DT_FLAGS"); declare_integer("DT_ENCODING"); declare_integer("STT_NOTYPE"); declare_integer("STT_OBJECT"); declare_integer("STT_FUNC"); declare_integer("STT_SECTION"); declare_integer("STT_FILE"); declare_integer("STT_COMMON"); declare_integer("STT_TLS"); declare_integer("STB_LOCAL"); declare_integer("STB_GLOBAL"); declare_integer("STB_WEAK"); declare_integer("PF_X"); declare_integer("PF_W"); declare_integer("PF_R"); begin_struct_array("segments") declare_integer("type"); declare_integer("flags"); declare_integer("offset"); declare_integer("virtual_address"); declare_integer("physical_address"); declare_integer("file_size"); declare_integer("memory_size"); declare_integer("alignment"); end_struct_array("segments") declare_integer("dynamic_section_entries"); begin_struct_array("dynamic") declare_integer("type"); declare_integer("val"); end_struct_array("dynamic") declare_integer("symtab_entries"); begin_struct_array("symtab") declare_string("name"); declare_integer("value"); declare_integer("size"); declare_integer("type"); declare_integer("bind"); declare_integer("shndx"); end_struct_array("symtab") declare_integer("dynsym_entries"); begin_struct_array("dynsym") declare_string("name"); declare_integer("value"); declare_integer("size"); declare_integer("type"); declare_integer("bind"); declare_integer("shndx"); end_struct_array("dynsym") end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; elf32_header_t* elf_header32; elf64_header_t* elf_header64; set_integer(ELF_ET_NONE, module_object, "ET_NONE"); set_integer(ELF_ET_REL, module_object, "ET_REL"); set_integer(ELF_ET_EXEC, module_object, "ET_EXEC"); set_integer(ELF_ET_DYN, module_object, "ET_DYN"); set_integer(ELF_ET_CORE, module_object, "ET_CORE"); set_integer(ELF_EM_NONE, module_object, "EM_NONE"); set_integer(ELF_EM_M32, module_object, "EM_M32"); set_integer(ELF_EM_SPARC, module_object, "EM_SPARC"); set_integer(ELF_EM_386, module_object, "EM_386"); set_integer(ELF_EM_68K, module_object, "EM_68K"); set_integer(ELF_EM_88K, module_object, "EM_88K"); set_integer(ELF_EM_860, module_object, "EM_860"); set_integer(ELF_EM_MIPS, module_object, "EM_MIPS"); set_integer(ELF_EM_MIPS_RS3_LE, module_object, "EM_MIPS_RS3_LE"); set_integer(ELF_EM_PPC, module_object, "EM_PPC"); set_integer(ELF_EM_PPC64, module_object, "EM_PPC64"); set_integer(ELF_EM_ARM, module_object, "EM_ARM"); set_integer(ELF_EM_X86_64, module_object, "EM_X86_64"); set_integer(ELF_EM_AARCH64, module_object, "EM_AARCH64"); set_integer(ELF_SHT_NULL, module_object, "SHT_NULL"); set_integer(ELF_SHT_PROGBITS, module_object, "SHT_PROGBITS"); set_integer(ELF_SHT_SYMTAB, module_object, "SHT_SYMTAB"); set_integer(ELF_SHT_STRTAB, module_object, "SHT_STRTAB"); set_integer(ELF_SHT_RELA, module_object, "SHT_RELA"); set_integer(ELF_SHT_HASH, module_object, "SHT_HASH"); set_integer(ELF_SHT_DYNAMIC, module_object, "SHT_DYNAMIC"); set_integer(ELF_SHT_NOTE, module_object, "SHT_NOTE"); set_integer(ELF_SHT_NOBITS, module_object, "SHT_NOBITS"); set_integer(ELF_SHT_REL, module_object, "SHT_REL"); set_integer(ELF_SHT_SHLIB, module_object, "SHT_SHLIB"); set_integer(ELF_SHT_DYNSYM, module_object, "SHT_DYNSYM"); set_integer(ELF_SHF_WRITE, module_object, "SHF_WRITE"); set_integer(ELF_SHF_ALLOC, module_object, "SHF_ALLOC"); set_integer(ELF_SHF_EXECINSTR, module_object, "SHF_EXECINSTR"); set_integer(ELF_PT_NULL, module_object, "PT_NULL"); set_integer(ELF_PT_LOAD, module_object, "PT_LOAD"); set_integer(ELF_PT_DYNAMIC, module_object, "PT_DYNAMIC"); set_integer(ELF_PT_INTERP, module_object, "PT_INTERP"); set_integer(ELF_PT_NOTE, module_object, "PT_NOTE"); set_integer(ELF_PT_SHLIB, module_object, "PT_SHLIB"); set_integer(ELF_PT_PHDR, module_object, "PT_PHDR"); set_integer(ELF_PT_TLS, module_object, "PT_TLS"); set_integer(ELF_PT_GNU_EH_FRAME, module_object, "PT_GNU_EH_FRAME"); set_integer(ELF_PT_GNU_STACK, module_object, "PT_GNU_STACK"); set_integer(ELF_DT_NULL, module_object, "DT_NULL"); set_integer(ELF_DT_NEEDED, module_object, "DT_NEEDED"); set_integer(ELF_DT_PLTRELSZ, module_object, "DT_PLTRELSZ"); set_integer(ELF_DT_PLTGOT, module_object, "DT_PLTGOT"); set_integer(ELF_DT_HASH, module_object, "DT_HASH"); set_integer(ELF_DT_STRTAB, module_object, "DT_STRTAB"); set_integer(ELF_DT_SYMTAB, module_object, "DT_SYMTAB"); set_integer(ELF_DT_RELA, module_object, "DT_RELA"); set_integer(ELF_DT_RELASZ, module_object, "DT_RELASZ"); set_integer(ELF_DT_RELAENT, module_object, "DT_RELAENT"); set_integer(ELF_DT_STRSZ, module_object, "DT_STRSZ"); set_integer(ELF_DT_SYMENT, module_object, "DT_SYMENT"); set_integer(ELF_DT_INIT, module_object, "DT_INIT"); set_integer(ELF_DT_FINI, module_object, "DT_FINI"); set_integer(ELF_DT_SONAME, module_object, "DT_SONAME"); set_integer(ELF_DT_RPATH, module_object, "DT_RPATH"); set_integer(ELF_DT_SYMBOLIC, module_object, "DT_SYMBOLIC"); set_integer(ELF_DT_REL, module_object, "DT_REL"); set_integer(ELF_DT_RELSZ, module_object, "DT_RELSZ"); set_integer(ELF_DT_RELENT, module_object, "DT_RELENT"); set_integer(ELF_DT_PLTREL, module_object, "DT_PLTREL"); set_integer(ELF_DT_DEBUG, module_object, "DT_DEBUG"); set_integer(ELF_DT_TEXTREL, module_object, "DT_TEXTREL"); set_integer(ELF_DT_JMPREL, module_object, "DT_JMPREL"); set_integer(ELF_DT_BIND_NOW, module_object, "DT_BIND_NOW"); set_integer(ELF_DT_INIT_ARRAY, module_object, "DT_INIT_ARRAY"); set_integer(ELF_DT_FINI_ARRAY, module_object, "DT_FINI_ARRAY"); set_integer(ELF_DT_INIT_ARRAYSZ, module_object, "DT_INIT_ARRAYSZ"); set_integer(ELF_DT_FINI_ARRAYSZ, module_object, "DT_FINI_ARRAYSZ"); set_integer(ELF_DT_RUNPATH, module_object, "DT_RUNPATH"); set_integer(ELF_DT_FLAGS, module_object, "DT_FLAGS"); set_integer(ELF_DT_ENCODING, module_object, "DT_ENCODING"); set_integer(ELF_STT_NOTYPE, module_object, "STT_NOTYPE"); set_integer(ELF_STT_OBJECT, module_object, "STT_OBJECT"); set_integer(ELF_STT_FUNC, module_object, "STT_FUNC"); set_integer(ELF_STT_SECTION, module_object, "STT_SECTION"); set_integer(ELF_STT_FILE, module_object, "STT_FILE"); set_integer(ELF_STT_COMMON, module_object, "STT_COMMON"); set_integer(ELF_STT_TLS, module_object, "STT_TLS"); set_integer(ELF_STB_LOCAL, module_object, "STB_LOCAL"); set_integer(ELF_STB_GLOBAL, module_object, "STB_GLOBAL"); set_integer(ELF_STB_WEAK, module_object, "STB_WEAK"); set_integer(ELF_PF_X, module_object, "PF_X"); set_integer(ELF_PF_W, module_object, "PF_W"); set_integer(ELF_PF_R, module_object, "PF_R"); foreach_memory_block(iterator, block) { const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL) continue; switch (get_elf_class_data(block_data, block->size)) { case CLASS_DATA(ELF_CLASS_32, ELF_DATA_2LSB): if (block->size > sizeof(elf32_header_t)) { elf_header32 = (elf32_header_t*) block_data; if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) || yr_le16toh(elf_header32->type) == ELF_ET_EXEC) { parse_elf_header_32_le( elf_header32, block->base, block->size, context->flags, module_object); } } break; case CLASS_DATA(ELF_CLASS_32, ELF_DATA_2MSB): if (block->size > sizeof(elf32_header_t)) { elf_header32 = (elf32_header_t*) block_data; if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) || yr_be16toh(elf_header32->type) == ELF_ET_EXEC) { parse_elf_header_32_be( elf_header32, block->base, block->size, context->flags, module_object); } } break; case CLASS_DATA(ELF_CLASS_64, ELF_DATA_2LSB): if (block->size > sizeof(elf64_header_t)) { elf_header64 = (elf64_header_t*) block_data; if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) || yr_le16toh(elf_header64->type) == ELF_ET_EXEC) { parse_elf_header_64_le( elf_header64, block->base, block->size, context->flags, module_object); } } break; case CLASS_DATA(ELF_CLASS_64, ELF_DATA_2MSB): if (block->size > sizeof(elf64_header_t)) { elf_header64 = (elf64_header_t*) block_data; if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) || yr_be16toh(elf_header64->type) == ELF_ET_EXEC) { parse_elf_header_64_be( elf_header64, block->base, block->size, context->flags, module_object); } } break; } } return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/hash/000077500000000000000000000000001413423160300164665ustar00rootroot00000000000000yara-4.1.3/libyara/modules/hash/hash.c000066400000000000000000000562061413423160300175660ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "../crypto.h" #define MODULE_NAME hash typedef struct _CACHE_KEY { int64_t offset; int64_t length; } CACHE_KEY; const uint32_t crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; static void digest_to_ascii( unsigned char* digest, char* digest_ascii, size_t digest_length) { size_t i; for (i = 0; i < digest_length; i++) sprintf(digest_ascii + (i * 2), "%02x", digest[i]); digest_ascii[digest_length * 2] = '\0'; } static char* get_from_cache( YR_OBJECT* module_object, const char* ns, int64_t offset, int64_t length) { CACHE_KEY key; YR_HASH_TABLE* hash_table = (YR_HASH_TABLE*) module_object->data; key.offset = offset; key.length = length; char* result = (char*) yr_hash_table_lookup_raw_key( hash_table, &key, sizeof(key), ns); YR_DEBUG_FPRINTF( 2, stderr, "- %s(offset=%" PRIi64 " length=%" PRIi64 ") {} = %p\n", __FUNCTION__, offset, length, result); return result; } static int add_to_cache( YR_OBJECT* module_object, const char* ns, int64_t offset, int64_t length, const char* digest) { CACHE_KEY key; YR_HASH_TABLE* hash_table = (YR_HASH_TABLE*) module_object->data; char* copy = yr_strdup(digest); key.offset = offset; key.length = length; if (copy == NULL) return ERROR_INSUFFICIENT_MEMORY; int result = yr_hash_table_add_raw_key( hash_table, &key, sizeof(key), ns, (void*) copy); YR_DEBUG_FPRINTF( 2, stderr, "- %s(offset=%" PRIi64 " length=%" PRIi64 " digest=%s) {} = %d\n", __FUNCTION__, offset, length, digest, result); return result; } define_function(string_md5) { unsigned char digest[YR_MD5_LEN]; char digest_ascii[YR_MD5_LEN * 2 + 1]; yr_md5_ctx md5_context; SIZED_STRING* s = sized_string_argument(1); yr_md5_init(&md5_context); yr_md5_update(&md5_context, s->c_string, s->length); yr_md5_final(digest, &md5_context); digest_to_ascii(digest, digest_ascii, YR_MD5_LEN); YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = 0x%s // s->length=%u\n", __FUNCTION__, digest_ascii, s->length); return_string(digest_ascii); } define_function(string_sha256) { unsigned char digest[YR_SHA256_LEN]; char digest_ascii[YR_SHA256_LEN * 2 + 1]; yr_sha256_ctx sha256_context; SIZED_STRING* s = sized_string_argument(1); yr_sha256_init(&sha256_context); yr_sha256_update(&sha256_context, s->c_string, s->length); yr_sha256_final(digest, &sha256_context); digest_to_ascii(digest, digest_ascii, YR_SHA256_LEN); YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = 0x%s // s->length=%u\n", __FUNCTION__, digest_ascii, s->length); return_string(digest_ascii); } define_function(string_sha1) { unsigned char digest[YR_SHA1_LEN]; char digest_ascii[YR_SHA1_LEN * 2 + 1]; yr_sha1_ctx sha_context; SIZED_STRING* s = sized_string_argument(1); yr_sha1_init(&sha_context); yr_sha1_update(&sha_context, s->c_string, s->length); yr_sha1_final(digest, &sha_context); digest_to_ascii(digest, digest_ascii, YR_SHA1_LEN); YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = 0x%s // s->length=%u\n", __FUNCTION__, digest_ascii, s->length); return_string(digest_ascii); } define_function(string_checksum32) { size_t i; SIZED_STRING* s = sized_string_argument(1); uint32_t checksum = 0; for (i = 0; i < s->length; i++) checksum += (uint8_t)(s->c_string[i]); YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = 0x%x // s->length=%u\n", __FUNCTION__, checksum, s->length); return_integer(checksum); } define_function(data_md5) { yr_md5_ctx md5_context; unsigned char digest[YR_MD5_LEN]; char digest_ascii[YR_MD5_LEN * 2 + 1]; char* cached_ascii_digest; bool past_first_block = false; YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; int64_t arg_offset = integer_argument(1); // offset where to start int64_t arg_length = integer_argument(2); // length of bytes we want hash on int64_t offset = arg_offset; int64_t length = arg_length; YR_DEBUG_FPRINTF( 2, stderr, "+ %s(offset=%" PRIi64 " length=%" PRIi64 ") {\n", __FUNCTION__, offset, length); if (block == NULL) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // block == NULL\n", __FUNCTION__); return_string(YR_UNDEFINED); } yr_md5_init(&md5_context); if (offset < 0 || length < 0 || offset < block->base) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // bad offset / length\n", __FUNCTION__); return_string(YR_UNDEFINED); } cached_ascii_digest = get_from_cache(module(), "md5", arg_offset, arg_length); if (cached_ascii_digest != NULL) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = %s (cached)\n", __FUNCTION__, cached_ascii_digest); return_string(cached_ascii_digest); } foreach_memory_block(iterator, block) { // if desired block within current block if (offset >= block->base && offset < block->base + block->size) { const uint8_t* block_data = block->fetch_data(block); if (block_data != NULL) { size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min( length, (size_t)(block->size - data_offset)); offset += data_len; length -= data_len; yr_md5_update(&md5_context, block_data + data_offset, data_len); } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // past_first_block\n", __FUNCTION__); return_string(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // !past_first_block\n", __FUNCTION__); return_string(YR_UNDEFINED); } yr_md5_final(digest, &md5_context); digest_to_ascii(digest, digest_ascii, YR_MD5_LEN); FAIL_ON_ERROR( add_to_cache(module(), "md5", arg_offset, arg_length, digest_ascii)); YR_DEBUG_FPRINTF(2, stderr, "} // %s() = 0x%s\n", __FUNCTION__, digest_ascii); return_string(digest_ascii); } define_function(data_sha1) { yr_sha1_ctx sha_context; unsigned char digest[YR_SHA1_LEN]; char digest_ascii[YR_SHA1_LEN * 2 + 1]; char* cached_ascii_digest; int past_first_block = false; int64_t arg_offset = integer_argument(1); // offset where to start int64_t arg_length = integer_argument(2); // length of bytes we want hash on int64_t offset = arg_offset; int64_t length = arg_length; YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; YR_DEBUG_FPRINTF( 2, stderr, "+ %s(offset=%" PRIi64 " length=%" PRIi64 ") {\n", __FUNCTION__, offset, length); if (block == NULL) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // block == NULL\n", __FUNCTION__); return_string(YR_UNDEFINED); } yr_sha1_init(&sha_context); if (offset < 0 || length < 0 || offset < block->base) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // bad offset / length\n", __FUNCTION__); return_string(YR_UNDEFINED); } cached_ascii_digest = get_from_cache( module(), "sha1", arg_offset, arg_length); if (cached_ascii_digest != NULL) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = %s (cached)\n", __FUNCTION__, cached_ascii_digest); return_string(cached_ascii_digest); } foreach_memory_block(iterator, block) { // if desired block within current block if (offset >= block->base && offset < block->base + block->size) { const uint8_t* block_data = block->fetch_data(block); if (block_data != NULL) { size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min( length, (size_t) block->size - data_offset); offset += data_len; length -= data_len; yr_sha1_update(&sha_context, block_data + data_offset, data_len); } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // past_first_block\n", __FUNCTION__); return_string(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // !past_first_block\n", __FUNCTION__); return_string(YR_UNDEFINED); } yr_sha1_final(digest, &sha_context); digest_to_ascii(digest, digest_ascii, YR_SHA1_LEN); FAIL_ON_ERROR( add_to_cache(module(), "sha1", arg_offset, arg_length, digest_ascii)); YR_DEBUG_FPRINTF(2, stderr, "} // %s() = 0x%s\n", __FUNCTION__, digest_ascii); return_string(digest_ascii); } define_function(data_sha256) { yr_sha256_ctx sha256_context; unsigned char digest[YR_SHA256_LEN]; char digest_ascii[YR_SHA256_LEN * 2 + 1]; char* cached_ascii_digest; int past_first_block = false; int64_t arg_offset = integer_argument(1); // offset where to start int64_t arg_length = integer_argument(2); // length of bytes we want hash on int64_t offset = arg_offset; int64_t length = arg_length; YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; YR_DEBUG_FPRINTF( 2, stderr, "+ %s(offset=%" PRIi64 " length=%" PRIi64 ") {\n", __FUNCTION__, offset, length); if (block == NULL) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // block == NULL\n", __FUNCTION__); return_string(YR_UNDEFINED); } yr_sha256_init(&sha256_context); if (offset < 0 || length < 0 || offset < block->base) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // bad offset / length\n", __FUNCTION__); return_string(YR_UNDEFINED); } cached_ascii_digest = get_from_cache( module(), "sha256", arg_offset, arg_length); if (cached_ascii_digest != NULL) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = %s (cached)\n", __FUNCTION__, cached_ascii_digest); return_string(cached_ascii_digest); } foreach_memory_block(iterator, block) { // if desired block within current block if (offset >= block->base && offset < block->base + block->size) { const uint8_t* block_data = block->fetch_data(block); if (block_data != NULL) { size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min(length, block->size - data_offset); offset += data_len; length -= data_len; yr_sha256_update(&sha256_context, block_data + data_offset, data_len); } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // past_first_block\n", __FUNCTION__); return_string(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) { YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = YR_UNDEFINED // !past_first_block\n", __FUNCTION__); return_string(YR_UNDEFINED); } yr_sha256_final(digest, &sha256_context); digest_to_ascii(digest, digest_ascii, YR_SHA256_LEN); FAIL_ON_ERROR( add_to_cache(module(), "sha256", arg_offset, arg_length, digest_ascii)); YR_DEBUG_FPRINTF(2, stderr, "} // %s() = 0x%s\n", __FUNCTION__, digest_ascii); return_string(digest_ascii); } define_function(data_checksum32) { int64_t offset = integer_argument(1); // offset where to start int64_t length = integer_argument(2); // length of bytes we want hash on YR_DEBUG_FPRINTF( 2, stderr, "+ %s(offset=%" PRIi64 " length=%" PRIi64 ") {\n", __FUNCTION__, offset, length); YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; uint32_t checksum = 0; int past_first_block = false; if (block == NULL) return_integer(YR_UNDEFINED); if (offset < 0 || length < 0 || offset < block->base) return_integer(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { const uint8_t* block_data = block->fetch_data(block); if (block_data != NULL) { size_t i; size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min(length, block->size - data_offset); offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) checksum += *(block_data + data_offset + i); } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. return_integer(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) return_integer(YR_UNDEFINED); YR_DEBUG_FPRINTF(2, stderr, "} // %s() = 0x%x\n", __FUNCTION__, checksum); return_integer(checksum); } define_function(string_crc32) { size_t i; SIZED_STRING* s = sized_string_argument(1); uint32_t checksum = 0xFFFFFFFF; for (i = 0; i < s->length; i++) checksum = crc32_tab[(checksum ^ (uint8_t) s->c_string[i]) & 0xFF] ^ (checksum >> 8); YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = 0x%x // s->length=%u\n", __FUNCTION__, checksum ^ 0xFFFFFFFF, s->length); return_integer(checksum ^ 0xFFFFFFFF); } define_function(data_crc32) { int64_t offset = integer_argument(1); // offset where to start int64_t length = integer_argument(2); // length of bytes we want hash on uint32_t checksum = 0xFFFFFFFF; YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; YR_DEBUG_FPRINTF( 2, stderr, "+ %s(offset=%" PRIi64 " length=%" PRIi64 ") {\n", __FUNCTION__, offset, length); int past_first_block = false; if (block == NULL) return_integer(YR_UNDEFINED); if (offset < 0 || length < 0 || offset < block->base) return_integer(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { const uint8_t* block_data = block->fetch_data(block); if (block_data != NULL) { size_t i; size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min(length, block->size - data_offset); offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) checksum = crc32_tab[(checksum ^ *(block_data + data_offset + i)) & 0xFF] ^ (checksum >> 8); } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. return_integer(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) return_integer(YR_UNDEFINED); YR_DEBUG_FPRINTF( 2, stderr, "} // %s() = 0x%x\n", __FUNCTION__, checksum ^ 0xFFFFFFFF); return_integer(checksum ^ 0xFFFFFFFF); } begin_declarations declare_function("md5", "ii", "s", data_md5); declare_function("md5", "s", "s", string_md5); declare_function("sha1", "ii", "s", data_sha1); declare_function("sha1", "s", "s", string_sha1); declare_function("sha256", "ii", "s", data_sha256); declare_function("sha256", "s", "s", string_sha256); declare_function("checksum32", "ii", "i", data_checksum32); declare_function("checksum32", "s", "i", string_checksum32); declare_function("crc32", "ii", "i", data_crc32); declare_function("crc32", "s", "i", string_crc32); end_declarations int module_initialize(YR_MODULE* module) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {}\n", __FUNCTION__); return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {}\n", __FUNCTION__); return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {}\n", __FUNCTION__); YR_HASH_TABLE* hash_table; FAIL_ON_ERROR(yr_hash_table_create(17, &hash_table)); module_object->data = hash_table; return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {}\n", __FUNCTION__); YR_HASH_TABLE* hash_table = (YR_HASH_TABLE*) module_object->data; if (hash_table != NULL) yr_hash_table_destroy(hash_table, (YR_HASH_TABLE_FREE_VALUE_FUNC) yr_free); return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/macho/000077500000000000000000000000001413423160300166325ustar00rootroot00000000000000yara-4.1.3/libyara/modules/macho/macho.c000066400000000000000000001300101413423160300200600ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define MODULE_NAME macho // Check for Mach-O binary magic constant. int is_macho_file_block(const uint32_t* magic) { return *magic == MH_MAGIC || *magic == MH_CIGAM || *magic == MH_MAGIC_64 || *magic == MH_CIGAM_64; } // Check if file is for 32-bit architecture. int macho_is_32(const uint8_t* magic) { // Magic must be [CE]FAEDFE or FEEDFA[CE]. return magic[0] == 0xce || magic[3] == 0xce; } // Check if file is for big-endian architecture. int macho_is_big(const uint8_t* magic) { // Magic must be [FE]EDFACE or [FE]EDFACF. return magic[0] == 0xfe; } // Check for Mach-O fat binary magic constant. int is_fat_macho_file_block(const uint32_t* magic) { return *magic == FAT_MAGIC || *magic == FAT_CIGAM || *magic == FAT_MAGIC_64 || *magic == FAT_CIGAM_64; } // Check if file is 32-bit fat file. int macho_fat_is_32(const uint8_t* magic) { // Magic must be CAFEBA[BE]. return magic[3] == 0xbe; } static int should_swap_bytes(const uint32_t magic) { return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM || magic == FAT_CIGAM_64; } static void swap_mach_header(yr_mach_header_64_t* mh) { // Don't swap the magic number so we can tell if swapping is needed mh->cputype = yr_bswap32(mh->cputype); mh->cpusubtype = yr_bswap32(mh->cpusubtype); mh->filetype = yr_bswap32(mh->filetype); mh->ncmds = yr_bswap32(mh->ncmds); mh->sizeofcmds = yr_bswap32(mh->sizeofcmds); mh->flags = yr_bswap32(mh->flags); if (!macho_is_32((const uint8_t*) &mh->magic)) mh->reserved = yr_bswap32(mh->reserved); } static void swap_load_command(yr_load_command_t* lc) { lc->cmd = yr_bswap32(lc->cmd); lc->cmdsize = yr_bswap32(lc->cmdsize); } static void swap_segment_command(yr_segment_command_32_t* sg) { sg->cmd = yr_bswap32(sg->cmd); sg->cmdsize = yr_bswap32(sg->cmdsize); sg->vmaddr = yr_bswap32(sg->vmaddr); sg->vmsize = yr_bswap32(sg->vmsize); sg->fileoff = yr_bswap32(sg->fileoff); sg->filesize = yr_bswap32(sg->filesize); sg->maxprot = yr_bswap32(sg->maxprot); sg->initprot = yr_bswap32(sg->initprot); sg->nsects = yr_bswap32(sg->nsects); sg->flags = yr_bswap32(sg->flags); } static void swap_segment_command_64(yr_segment_command_64_t* sg) { sg->cmd = yr_bswap32(sg->cmd); sg->cmdsize = yr_bswap32(sg->cmdsize); sg->vmaddr = yr_bswap64(sg->vmaddr); sg->vmsize = yr_bswap64(sg->vmsize); sg->fileoff = yr_bswap64(sg->fileoff); sg->filesize = yr_bswap64(sg->filesize); sg->maxprot = yr_bswap32(sg->maxprot); sg->initprot = yr_bswap32(sg->initprot); sg->nsects = yr_bswap32(sg->nsects); sg->flags = yr_bswap32(sg->flags); } static void swap_section(yr_section_32_t* sec) { sec->addr = yr_bswap32(sec->addr); sec->size = yr_bswap32(sec->size); sec->offset = yr_bswap32(sec->offset); sec->align = yr_bswap32(sec->align); sec->reloff = yr_bswap32(sec->reloff); sec->nreloc = yr_bswap32(sec->nreloc); sec->flags = yr_bswap32(sec->flags); sec->reserved1 = yr_bswap32(sec->reserved1); sec->reserved2 = yr_bswap32(sec->reserved2); } static void swap_section_64(yr_section_64_t* sec) { sec->addr = yr_bswap64(sec->addr); sec->size = yr_bswap64(sec->size); sec->offset = yr_bswap32(sec->offset); sec->align = yr_bswap32(sec->align); sec->reloff = yr_bswap32(sec->reloff); sec->nreloc = yr_bswap32(sec->nreloc); sec->flags = yr_bswap32(sec->flags); sec->reserved1 = yr_bswap32(sec->reserved1); sec->reserved2 = yr_bswap32(sec->reserved2); sec->reserved3 = yr_bswap32(sec->reserved3); } static void swap_entry_point_command(yr_entry_point_command_t* ep_command) { ep_command->cmd = yr_bswap32(ep_command->cmd); ep_command->cmdsize = yr_bswap32(ep_command->cmdsize); ep_command->entryoff = yr_bswap64(ep_command->entryoff); ep_command->stacksize = yr_bswap64(ep_command->stacksize); } // Convert virtual address to file offset. Segments have to be already loaded. bool macho_rva_to_offset(uint64_t address, uint64_t* result, YR_OBJECT* object) { uint64_t segment_count = get_integer(object, "number_of_segments"); for (int i = 0; i < segment_count; i++) { uint64_t start = get_integer(object, "segments[%i].vmaddr", i); uint64_t end = start + get_integer(object, "segments[%i].vmsize", i); if (address >= start && address < end) { uint64_t fileoff = get_integer(object, "segments[%i].fileoff", i); *result = fileoff + (address - start); return true; } } return false; } // Convert file offset to virtual address. Segments have to be already loaded. int macho_offset_to_rva(uint64_t offset, uint64_t* result, YR_OBJECT* object) { uint64_t segment_count = get_integer(object, "number_of_segments"); for (int i = 0; i < segment_count; i++) { uint64_t start = get_integer(object, "segments[%i].fileoff", i); uint64_t end = start + get_integer(object, "segments[%i].filesize", i); if (offset >= start && offset < end) { uint64_t vmaddr = get_integer(object, "segments[%i].vmaddr", i); *result = vmaddr + (offset - start); return true; } } return false; } // Get entry point address from LC_UNIXTHREAD load command. void macho_handle_unixthread( const uint8_t* data, size_t size, YR_OBJECT* object, YR_SCAN_CONTEXT* context) { int should_swap = should_swap_bytes(get_integer(object, "magic")); bool is64 = false; if (size < sizeof(yr_thread_command_t)) return; // command_size is the size indicated in yr_thread_command_t structure, but // limited to the data's size because we can't rely on the structure having a // valid size. uint32_t command_size = yr_min(size, ((yr_thread_command_t*) data)->cmdsize); // command_size should be at least the size of yr_thread_command_t. if (command_size < sizeof(yr_thread_command_t)) return; // command_size includes the size of yr_thread_command_t and the thread // state structure that follows, let's compute the size of the thread state // structure. size_t thread_state_size = command_size - sizeof(yr_thread_command_t); // The structure that contains the thread state starts where // yr_thread_command_t ends. const void* thread_state = data + sizeof(yr_thread_command_t); uint64_t address = 0; switch (get_integer(object, "cputype")) { case CPU_TYPE_MC680X0: { if (thread_state_size >= sizeof(yr_m68k_thread_state_t)) address = ((yr_m68k_thread_state_t*) thread_state)->pc; break; } case CPU_TYPE_MC88000: { if (thread_state_size >= sizeof(yr_m88k_thread_state_t)) address = ((yr_m88k_thread_state_t*) thread_state)->xip; break; } case CPU_TYPE_SPARC: { if (thread_state_size >= sizeof(yr_sparc_thread_state_t)) address = ((yr_sparc_thread_state_t*) thread_state)->pc; break; } case CPU_TYPE_POWERPC: { if (thread_state_size >= sizeof(yr_ppc_thread_state_t)) address = ((yr_ppc_thread_state_t*) thread_state)->srr0; break; } case CPU_TYPE_X86: { if (thread_state_size >= sizeof(yr_x86_thread_state_t)) address = ((yr_x86_thread_state_t*) thread_state)->eip; break; } case CPU_TYPE_ARM: { if (thread_state_size >= sizeof(yr_arm_thread_state_t)) address = ((yr_arm_thread_state_t*) thread_state)->pc; break; } case CPU_TYPE_X86_64: { if (thread_state_size >= sizeof(yr_x86_thread_state64_t)) address = ((yr_x86_thread_state64_t*) thread_state)->rip; is64 = true; break; } case CPU_TYPE_ARM64: { if (thread_state_size >= sizeof(yr_arm_thread_state64_t)) address = ((yr_arm_thread_state64_t*) thread_state)->pc; is64 = true; break; } case CPU_TYPE_POWERPC64: { if (thread_state_size >= sizeof(yr_ppc_thread_state64_t)) address = ((yr_ppc_thread_state64_t*) thread_state)->srr0; is64 = true; break; } default: return; } if (should_swap) { if (is64) address = yr_bswap64(address); else address = yr_bswap32(address); } if (context->flags & SCAN_FLAGS_PROCESS_MEMORY) { set_integer(address, object, "entry_point"); } else { uint64_t offset = 0; if (macho_rva_to_offset(address, &offset, object)) { set_integer(offset, object, "entry_point"); } } } // Get entry point offset and stack-size from LC_MAIN load command. void macho_handle_main( void* data, size_t size, YR_OBJECT* object, YR_SCAN_CONTEXT* context) { yr_entry_point_command_t ep_command; if (size < sizeof(yr_entry_point_command_t)) return; memcpy(&ep_command, data, sizeof(yr_entry_point_command_t)); if (should_swap_bytes(get_integer(object, "magic"))) swap_entry_point_command(&ep_command); if (context->flags & SCAN_FLAGS_PROCESS_MEMORY) { uint64_t address = 0; if (macho_offset_to_rva(ep_command.entryoff, &address, object)) { set_integer(address, object, "entry_point"); } } else { set_integer(ep_command.entryoff, object, "entry_point"); } set_integer(ep_command.stacksize, object, "stack_size"); } // Load segment and its sections. void macho_handle_segment( const uint8_t* data, size_t size, const unsigned i, YR_OBJECT* object) { if (size < sizeof(yr_segment_command_32_t)) return; yr_segment_command_32_t sg; memcpy(&sg, data, sizeof(yr_segment_command_32_t)); int should_swap = should_swap_bytes(get_integer(object, "magic")); if (should_swap) swap_segment_command(&sg); set_sized_string( sg.segname, strnlen(sg.segname, 16), object, "segments[%i].segname", i); set_integer(sg.vmaddr, object, "segments[%i].vmaddr", i); set_integer(sg.vmsize, object, "segments[%i].vmsize", i); set_integer(sg.fileoff, object, "segments[%i].fileoff", i); set_integer(sg.filesize, object, "segments[%i].fsize", i); set_integer(sg.maxprot, object, "segments[%i].maxprot", i); set_integer(sg.initprot, object, "segments[%i].initprot", i); set_integer(sg.nsects, object, "segments[%i].nsects", i); set_integer(sg.flags, object, "segments[%i].flags", i); uint64_t parsed_size = sizeof(yr_segment_command_32_t); // The array of yr_section_32_t starts where yr_segment_command_32_t ends. yr_section_32_t* sections = (yr_section_32_t*) (data + sizeof(yr_segment_command_32_t)); for (unsigned j = 0; j < sg.nsects; ++j) { yr_section_32_t sec; parsed_size += sizeof(yr_section_32_t); if (sg.cmdsize < parsed_size) break; memcpy(&sec, §ions[j], sizeof(yr_section_32_t)); if (should_swap) swap_section(&sec); set_sized_string( sec.segname, strnlen(sec.segname, 16), object, "segments[%i].sections[%i].segname", i, j); set_sized_string( sec.sectname, strnlen(sec.sectname, 16), object, "segments[%i].sections[%i].sectname", i, j); set_integer(sec.addr, object, "segments[%i].sections[%i].addr", i, j); set_integer(sec.size, object, "segments[%i].sections[%i].size", i, j); set_integer(sec.offset, object, "segments[%i].sections[%i].offset", i, j); set_integer(sec.align, object, "segments[%i].sections[%i].align", i, j); set_integer(sec.reloff, object, "segments[%i].sections[%i].reloff", i, j); set_integer(sec.nreloc, object, "segments[%i].sections[%i].nreloc", i, j); set_integer(sec.flags, object, "segments[%i].sections[%i].flags", i, j); set_integer( sec.reserved1, object, "segments[%i].sections[%i].reserved1", i, j); set_integer( sec.reserved2, object, "segments[%i].sections[%i].reserved2", i, j); } } void macho_handle_segment_64( const uint8_t* data, size_t size, const unsigned i, YR_OBJECT* object) { if (size < sizeof(yr_segment_command_64_t)) return; yr_segment_command_64_t sg; memcpy(&sg, data, sizeof(yr_segment_command_64_t)); int should_swap = should_swap_bytes(get_integer(object, "magic")); if (should_swap) swap_segment_command_64(&sg); set_sized_string( sg.segname, strnlen(sg.segname, 16), object, "segments[%i].segname", i); set_integer(sg.vmaddr, object, "segments[%i].vmaddr", i); set_integer(sg.vmsize, object, "segments[%i].vmsize", i); set_integer(sg.fileoff, object, "segments[%i].fileoff", i); set_integer(sg.filesize, object, "segments[%i].fsize", i); set_integer(sg.maxprot, object, "segments[%i].maxprot", i); set_integer(sg.initprot, object, "segments[%i].initprot", i); set_integer(sg.nsects, object, "segments[%i].nsects", i); set_integer(sg.flags, object, "segments[%i].flags", i); uint64_t parsed_size = sizeof(yr_segment_command_64_t); yr_section_64_t sec; for (unsigned j = 0; j < sg.nsects; ++j) { parsed_size += sizeof(yr_section_64_t); if (sg.cmdsize < parsed_size) break; memcpy( &sec, data + sizeof(yr_segment_command_64_t) + (j * sizeof(yr_section_64_t)), sizeof(yr_section_64_t)); if (should_swap) swap_section_64(&sec); set_sized_string( sec.segname, strnlen(sec.segname, 16), object, "segments[%i].sections[%i].segname", i, j); set_sized_string( sec.sectname, strnlen(sec.sectname, 16), object, "segments[%i].sections[%i].sectname", i, j); set_integer(sec.addr, object, "segments[%i].sections[%i].addr", i, j); set_integer(sec.size, object, "segments[%i].sections[%i].size", i, j); set_integer(sec.offset, object, "segments[%i].sections[%i].offset", i, j); set_integer(sec.align, object, "segments[%i].sections[%i].align", i, j); set_integer(sec.reloff, object, "segments[%i].sections[%i].reloff", i, j); set_integer(sec.nreloc, object, "segments[%i].sections[%i].nreloc", i, j); set_integer(sec.flags, object, "segments[%i].sections[%i].flags", i, j); set_integer( sec.reserved1, object, "segments[%i].sections[%i].reserved1", i, j); set_integer( sec.reserved2, object, "segments[%i].sections[%i].reserved2", i, j); set_integer( sec.reserved3, object, "segments[%i].sections[%i].reserved3", i, j); } } // Parse Mach-O file. void macho_parse_file( const uint8_t* data, const uint64_t size, YR_OBJECT* object, YR_SCAN_CONTEXT* context) { // Size must be large enough the hold yr_mach_header_64_t, which is larger // than yr_mach_header_32_t. if (size < sizeof(yr_mach_header_64_t)) return; size_t header_size = macho_is_32(data) ? sizeof(yr_mach_header_32_t) : sizeof(yr_mach_header_64_t); // yr_mach_header_64_t is used for storing the header for both for 32-bits and // 64-bits files. yr_mach_header_64_t is exactly like yr_mach_header_32_t // but with an extra "reserved" field at the end. yr_mach_header_64_t header; memcpy(&header, data, header_size); int should_swap = should_swap_bytes(header.magic); if (should_swap) swap_mach_header(&header); set_integer(header.magic, object, "magic"); set_integer(header.cputype, object, "cputype"); set_integer(header.cpusubtype, object, "cpusubtype"); set_integer(header.filetype, object, "filetype"); set_integer(header.ncmds, object, "ncmds"); set_integer(header.sizeofcmds, object, "sizeofcmds"); set_integer(header.flags, object, "flags"); // The "reserved" field exists only in 64 bits files. if (!macho_is_32(data)) set_integer(header.reserved, object, "reserved"); // The first command parsing pass handles only segments. uint64_t seg_count = 0; uint64_t parsed_size = header_size; uint8_t* command = (uint8_t*) (data + header_size); yr_load_command_t command_struct; for (unsigned i = 0; i < header.ncmds; i++) { if (data + size < command + sizeof(yr_load_command_t)) break; memcpy(&command_struct, command, sizeof(yr_load_command_t)); if (should_swap) swap_load_command(&command_struct); if (size - parsed_size < command_struct.cmdsize) break; if (command_struct.cmdsize < sizeof(yr_load_command_t)) break; switch (command_struct.cmd) { case LC_SEGMENT: macho_handle_segment(command, size - parsed_size, seg_count++, object); break; case LC_SEGMENT_64: macho_handle_segment_64(command, size - parsed_size, seg_count++, object); break; } command += command_struct.cmdsize; parsed_size += command_struct.cmdsize; } set_integer(seg_count, object, "number_of_segments"); // The second command parsing pass handles others, who use segment count. parsed_size = header_size; command = (uint8_t*) (data + header_size); for (unsigned i = 0; i < header.ncmds; i++) { if (data + size < command + sizeof(yr_load_command_t)) break; memcpy(&command_struct, command, sizeof(yr_load_command_t)); if (should_swap) swap_load_command(&command_struct); if (size - parsed_size < command_struct.cmdsize) break; if (command_struct.cmdsize < sizeof(yr_load_command_t)) break; switch (command_struct.cmd) { case LC_UNIXTHREAD: macho_handle_unixthread(command, size - parsed_size, object, context); break; case LC_MAIN: macho_handle_main(command, size - parsed_size, object, context); break; } command += command_struct.cmdsize; parsed_size += command_struct.cmdsize; } } // Parse Mach-O fat file. void macho_load_fat_arch_header( const uint8_t* data, const uint64_t size, uint32_t num, yr_fat_arch_64_t* arch) { if (macho_fat_is_32(data)) { yr_fat_arch_32_t* arch32 = (yr_fat_arch_32_t*) (data + sizeof(yr_fat_header_t) + (num * sizeof(yr_fat_arch_32_t))); arch->cputype = yr_be32toh(arch32->cputype); arch->cpusubtype = yr_be32toh(arch32->cpusubtype); arch->offset = yr_be32toh(arch32->offset); arch->size = yr_be32toh(arch32->size); arch->align = yr_be32toh(arch32->align); arch->reserved = 0; } else { yr_fat_arch_64_t* arch64 = (yr_fat_arch_64_t*) (data + sizeof(yr_fat_header_t) + (num * sizeof(yr_fat_arch_64_t))); arch->cputype = yr_be32toh(arch64->cputype); arch->cpusubtype = yr_be32toh(arch64->cpusubtype); arch->offset = yr_be64toh(arch64->offset); arch->size = yr_be64toh(arch64->size); arch->align = yr_be32toh(arch64->align); arch->reserved = yr_be32toh(arch64->reserved); } } void macho_parse_fat_file( const uint8_t* data, const uint64_t size, YR_OBJECT* object, YR_SCAN_CONTEXT* context) { size_t fat_arch_sz = sizeof(yr_fat_arch_64_t); if (macho_fat_is_32(data)) fat_arch_sz = sizeof(yr_fat_arch_32_t); if (size < sizeof(yr_fat_header_t)) return; /* All data in Mach-O fat binary headers are in big-endian byte order. */ const yr_fat_header_t* header = (yr_fat_header_t*) data; set_integer(yr_be32toh(header->magic), object, "fat_magic"); uint32_t count = yr_be32toh(header->nfat_arch); set_integer(count, object, "nfat_arch"); if (size < sizeof(yr_fat_header_t) + count * fat_arch_sz) return; yr_fat_arch_64_t arch; for (uint32_t i = 0; i < count; i++) { macho_load_fat_arch_header(data, size, i, &arch); set_integer(arch.cputype, object, "fat_arch[%i].cputype", i); set_integer(arch.cpusubtype, object, "fat_arch[%i].cpusubtype", i); set_integer(arch.offset, object, "fat_arch[%i].offset", i); set_integer(arch.size, object, "fat_arch[%i].size", i); set_integer(arch.align, object, "fat_arch[%i].align", i); set_integer(arch.reserved, object, "fat_arch[%i].reserved", i); // Check for integer overflow. if (arch.offset + arch.size < arch.offset) continue; if (size < arch.offset + arch.size) continue; // Force 'file' array entry creation. set_integer(YR_UNDEFINED, object, "file[%i].magic", i); // Get specific Mach-O file data. macho_parse_file( data + arch.offset, arch.size, get_object(object, "file[%i]", i), context); } } // Sets all necessary Mach-O constants and definitions. void macho_set_definitions(YR_OBJECT* object) { // Magic constants set_integer(MH_MAGIC, object, "MH_MAGIC"); set_integer(MH_CIGAM, object, "MH_CIGAM"); set_integer(MH_MAGIC_64, object, "MH_MAGIC_64"); set_integer(MH_CIGAM_64, object, "MH_CIGAM_64"); // Fat magic constants set_integer(FAT_MAGIC, object, "FAT_MAGIC"); set_integer(FAT_CIGAM, object, "FAT_CIGAM"); set_integer(FAT_MAGIC_64, object, "FAT_MAGIC_64"); set_integer(FAT_CIGAM_64, object, "FAT_CIGAM_64"); // 64-bit masks set_integer(CPU_ARCH_ABI64, object, "CPU_ARCH_ABI64"); set_integer(CPU_SUBTYPE_LIB64, object, "CPU_SUBTYPE_LIB64"); // CPU types set_integer(CPU_TYPE_MC680X0, object, "CPU_TYPE_MC680X0"); set_integer(CPU_TYPE_X86, object, "CPU_TYPE_X86"); set_integer(CPU_TYPE_X86, object, "CPU_TYPE_I386"); set_integer(CPU_TYPE_X86_64, object, "CPU_TYPE_X86_64"); set_integer(CPU_TYPE_MIPS, object, "CPU_TYPE_MIPS"); set_integer(CPU_TYPE_MC98000, object, "CPU_TYPE_MC98000"); set_integer(CPU_TYPE_ARM, object, "CPU_TYPE_ARM"); set_integer(CPU_TYPE_ARM64, object, "CPU_TYPE_ARM64"); set_integer(CPU_TYPE_MC88000, object, "CPU_TYPE_MC88000"); set_integer(CPU_TYPE_SPARC, object, "CPU_TYPE_SPARC"); set_integer(CPU_TYPE_POWERPC, object, "CPU_TYPE_POWERPC"); set_integer(CPU_TYPE_POWERPC64, object, "CPU_TYPE_POWERPC64"); // CPU sub-types set_integer( CPU_SUBTYPE_INTEL_MODEL_ALL, object, "CPU_SUBTYPE_INTEL_MODEL_ALL"); set_integer(CPU_SUBTYPE_386, object, "CPU_SUBTYPE_386"); set_integer(CPU_SUBTYPE_386, object, "CPU_SUBTYPE_I386_ALL"); set_integer(CPU_SUBTYPE_386, object, "CPU_SUBTYPE_X86_64_ALL"); set_integer(CPU_SUBTYPE_486, object, "CPU_SUBTYPE_486"); set_integer(CPU_SUBTYPE_486SX, object, "CPU_SUBTYPE_486SX"); set_integer(CPU_SUBTYPE_586, object, "CPU_SUBTYPE_586"); set_integer(CPU_SUBTYPE_PENT, object, "CPU_SUBTYPE_PENT"); set_integer(CPU_SUBTYPE_PENTPRO, object, "CPU_SUBTYPE_PENTPRO"); set_integer(CPU_SUBTYPE_PENTII_M3, object, "CPU_SUBTYPE_PENTII_M3"); set_integer(CPU_SUBTYPE_PENTII_M5, object, "CPU_SUBTYPE_PENTII_M5"); set_integer(CPU_SUBTYPE_CELERON, object, "CPU_SUBTYPE_CELERON"); set_integer(CPU_SUBTYPE_CELERON_MOBILE, object, "CPU_SUBTYPE_CELERON_MOBILE"); set_integer(CPU_SUBTYPE_PENTIUM_3, object, "CPU_SUBTYPE_PENTIUM_3"); set_integer(CPU_SUBTYPE_PENTIUM_3_M, object, "CPU_SUBTYPE_PENTIUM_3_M"); set_integer(CPU_SUBTYPE_PENTIUM_3_XEON, object, "CPU_SUBTYPE_PENTIUM_3_XEON"); set_integer(CPU_SUBTYPE_PENTIUM_M, object, "CPU_SUBTYPE_PENTIUM_M"); set_integer(CPU_SUBTYPE_PENTIUM_4, object, "CPU_SUBTYPE_PENTIUM_4"); set_integer(CPU_SUBTYPE_PENTIUM_4_M, object, "CPU_SUBTYPE_PENTIUM_4_M"); set_integer(CPU_SUBTYPE_ITANIUM, object, "CPU_SUBTYPE_ITANIUM"); set_integer(CPU_SUBTYPE_ITANIUM_2, object, "CPU_SUBTYPE_ITANIUM_2"); set_integer(CPU_SUBTYPE_XEON, object, "CPU_SUBTYPE_XEON"); set_integer(CPU_SUBTYPE_XEON_MP, object, "CPU_SUBTYPE_XEON_MP"); set_integer(CPU_SUBTYPE_ARM_ALL, object, "CPU_SUBTYPE_ARM_ALL"); set_integer(CPU_SUBTYPE_ARM_V4T, object, "CPU_SUBTYPE_ARM_V4T"); set_integer(CPU_SUBTYPE_ARM_V6, object, "CPU_SUBTYPE_ARM_V6"); set_integer(CPU_SUBTYPE_ARM_V5, object, "CPU_SUBTYPE_ARM_V5"); set_integer(CPU_SUBTYPE_ARM_V5TEJ, object, "CPU_SUBTYPE_ARM_V5TEJ"); set_integer(CPU_SUBTYPE_ARM_XSCALE, object, "CPU_SUBTYPE_ARM_XSCALE"); set_integer(CPU_SUBTYPE_ARM_V7, object, "CPU_SUBTYPE_ARM_V7"); set_integer(CPU_SUBTYPE_ARM_V7F, object, "CPU_SUBTYPE_ARM_V7F"); set_integer(CPU_SUBTYPE_ARM_V7S, object, "CPU_SUBTYPE_ARM_V7S"); set_integer(CPU_SUBTYPE_ARM_V7K, object, "CPU_SUBTYPE_ARM_V7K"); set_integer(CPU_SUBTYPE_ARM_V6M, object, "CPU_SUBTYPE_ARM_V6M"); set_integer(CPU_SUBTYPE_ARM_V7M, object, "CPU_SUBTYPE_ARM_V7M"); set_integer(CPU_SUBTYPE_ARM_V7EM, object, "CPU_SUBTYPE_ARM_V7EM"); set_integer(CPU_SUBTYPE_ARM64_ALL, object, "CPU_SUBTYPE_ARM64_ALL"); set_integer(CPU_SUBTYPE_SPARC_ALL, object, "CPU_SUBTYPE_SPARC_ALL"); set_integer(CPU_SUBTYPE_POWERPC_ALL, object, "CPU_SUBTYPE_POWERPC_ALL"); set_integer(CPU_SUBTYPE_MC980000_ALL, object, "CPU_SUBTYPE_MC980000_ALL"); set_integer(CPU_SUBTYPE_POWERPC_601, object, "CPU_SUBTYPE_POWERPC_601"); set_integer(CPU_SUBTYPE_MC98601, object, "CPU_SUBTYPE_MC98601"); set_integer(CPU_SUBTYPE_POWERPC_602, object, "CPU_SUBTYPE_POWERPC_602"); set_integer(CPU_SUBTYPE_POWERPC_603, object, "CPU_SUBTYPE_POWERPC_603"); set_integer(CPU_SUBTYPE_POWERPC_603e, object, "CPU_SUBTYPE_POWERPC_603e"); set_integer(CPU_SUBTYPE_POWERPC_603ev, object, "CPU_SUBTYPE_POWERPC_603ev"); set_integer(CPU_SUBTYPE_POWERPC_604, object, "CPU_SUBTYPE_POWERPC_604"); set_integer(CPU_SUBTYPE_POWERPC_604e, object, "CPU_SUBTYPE_POWERPC_604e"); set_integer(CPU_SUBTYPE_POWERPC_620, object, "CPU_SUBTYPE_POWERPC_620"); set_integer(CPU_SUBTYPE_POWERPC_750, object, "CPU_SUBTYPE_POWERPC_750"); set_integer(CPU_SUBTYPE_POWERPC_7400, object, "CPU_SUBTYPE_POWERPC_7400"); set_integer(CPU_SUBTYPE_POWERPC_7450, object, "CPU_SUBTYPE_POWERPC_7450"); set_integer(CPU_SUBTYPE_POWERPC_970, object, "CPU_SUBTYPE_POWERPC_970"); // File types set_integer(MH_OBJECT, object, "MH_OBJECT"); set_integer(MH_EXECUTE, object, "MH_EXECUTE"); set_integer(MH_FVMLIB, object, "MH_FVMLIB"); set_integer(MH_CORE, object, "MH_CORE"); set_integer(MH_PRELOAD, object, "MH_PRELOAD"); set_integer(MH_DYLIB, object, "MH_DYLIB"); set_integer(MH_DYLINKER, object, "MH_DYLINKER"); set_integer(MH_BUNDLE, object, "MH_BUNDLE"); set_integer(MH_DYLIB_STUB, object, "MH_DYLIB_STUB"); set_integer(MH_DSYM, object, "MH_DSYM"); set_integer(MH_KEXT_BUNDLE, object, "MH_KEXT_BUNDLE"); // Header flags set_integer(MH_NOUNDEFS, object, "MH_NOUNDEFS"); set_integer(MH_INCRLINK, object, "MH_INCRLINK"); set_integer(MH_DYLDLINK, object, "MH_DYLDLINK"); set_integer(MH_BINDATLOAD, object, "MH_BINDATLOAD"); set_integer(MH_PREBOUND, object, "MH_PREBOUND"); set_integer(MH_SPLIT_SEGS, object, "MH_SPLIT_SEGS"); set_integer(MH_LAZY_INIT, object, "MH_LAZY_INIT"); set_integer(MH_TWOLEVEL, object, "MH_TWOLEVEL"); set_integer(MH_FORCE_FLAT, object, "MH_FORCE_FLAT"); set_integer(MH_NOMULTIDEFS, object, "MH_NOMULTIDEFS"); set_integer(MH_NOFIXPREBINDING, object, "MH_NOFIXPREBINDING"); set_integer(MH_PREBINDABLE, object, "MH_PREBINDABLE"); set_integer(MH_ALLMODSBOUND, object, "MH_ALLMODSBOUND"); set_integer(MH_SUBSECTIONS_VIA_SYMBOLS, object, "MH_SUBSECTIONS_VIA_SYMBOLS"); set_integer(MH_CANONICAL, object, "MH_CANONICAL"); set_integer(MH_WEAK_DEFINES, object, "MH_WEAK_DEFINES"); set_integer(MH_BINDS_TO_WEAK, object, "MH_BINDS_TO_WEAK"); set_integer(MH_ALLOW_STACK_EXECUTION, object, "MH_ALLOW_STACK_EXECUTION"); set_integer(MH_ROOT_SAFE, object, "MH_ROOT_SAFE"); set_integer(MH_SETUID_SAFE, object, "MH_SETUID_SAFE"); set_integer(MH_NO_REEXPORTED_DYLIBS, object, "MH_NO_REEXPORTED_DYLIBS"); set_integer(MH_PIE, object, "MH_PIE"); set_integer(MH_DEAD_STRIPPABLE_DYLIB, object, "MH_DEAD_STRIPPABLE_DYLIB"); set_integer(MH_HAS_TLV_DESCRIPTORS, object, "MH_HAS_TLV_DESCRIPTORS"); set_integer(MH_NO_HEAP_EXECUTION, object, "MH_NO_HEAP_EXECUTION"); set_integer(MH_APP_EXTENSION_SAFE, object, "MH_APP_EXTENSION_SAFE"); // Segment flags masks set_integer(SG_HIGHVM, object, "SG_HIGHVM"); set_integer(SG_FVMLIB, object, "SG_FVMLIB"); set_integer(SG_NORELOC, object, "SG_NORELOC"); set_integer(SG_PROTECTED_VERSION_1, object, "SG_PROTECTED_VERSION_1"); // Section flags masks set_integer(SECTION_TYPE, object, "SECTION_TYPE"); set_integer(SECTION_ATTRIBUTES, object, "SECTION_ATTRIBUTES"); // Section types set_integer(S_REGULAR, object, "S_REGULAR"); set_integer(S_ZEROFILL, object, "S_ZEROFILL"); set_integer(S_CSTRING_LITERALS, object, "S_CSTRING_LITERALS"); set_integer(S_4BYTE_LITERALS, object, "S_4BYTE_LITERALS"); set_integer(S_8BYTE_LITERALS, object, "S_8BYTE_LITERALS"); set_integer(S_NON_LAZY_SYMBOL_POINTERS, object, "S_NON_LAZY_SYMBOL_POINTERS"); set_integer(S_LAZY_SYMBOL_POINTERS, object, "S_LAZY_SYMBOL_POINTERS"); set_integer(S_LITERAL_POINTERS, object, "S_LITERAL_POINTERS"); set_integer(S_SYMBOL_STUBS, object, "S_SYMBOL_STUBS"); set_integer(S_MOD_INIT_FUNC_POINTERS, object, "S_MOD_INIT_FUNC_POINTERS"); set_integer(S_MOD_TERM_FUNC_POINTERS, object, "S_MOD_TERM_FUNC_POINTERS"); set_integer(S_COALESCED, object, "S_COALESCED"); set_integer(S_GB_ZEROFILL, object, "S_GB_ZEROFILL"); set_integer(S_INTERPOSING, object, "S_INTERPOSING"); set_integer(S_16BYTE_LITERALS, object, "S_16BYTE_LITERALS"); set_integer(S_DTRACE_DOF, object, "S_DTRACE_DOF"); set_integer( S_LAZY_DYLIB_SYMBOL_POINTERS, object, "S_LAZY_DYLIB_SYMBOL_POINTERS"); set_integer(S_THREAD_LOCAL_REGULAR, object, "S_THREAD_LOCAL_REGULAR"); set_integer(S_THREAD_LOCAL_ZEROFILL, object, "S_THREAD_LOCAL_ZEROFILL"); set_integer(S_THREAD_LOCAL_VARIABLES, object, "S_THREAD_LOCAL_VARIABLES"); set_integer( S_THREAD_LOCAL_VARIABLE_POINTERS, object, "S_THREAD_LOCAL_VARIABLE_POINTERS"); set_integer( S_THREAD_LOCAL_INIT_FUNCTION_POINTERS, object, "S_THREAD_LOCAL_INIT_FUNCTION_POINTERS"); // Section attributes set_integer(S_ATTR_PURE_INSTRUCTIONS, object, "S_ATTR_PURE_INSTRUCTIONS"); set_integer(S_ATTR_NO_TOC, object, "S_ATTR_NO_TOC"); set_integer(S_ATTR_STRIP_STATIC_SYMS, object, "S_ATTR_STRIP_STATIC_SYMS"); set_integer(S_ATTR_NO_DEAD_STRIP, object, "S_ATTR_NO_DEAD_STRIP"); set_integer(S_ATTR_LIVE_SUPPORT, object, "S_ATTR_LIVE_SUPPORT"); set_integer(S_ATTR_SELF_MODIFYING_CODE, object, "S_ATTR_SELF_MODIFYING_CODE"); set_integer(S_ATTR_DEBUG, object, "S_ATTR_DEBUG"); set_integer(S_ATTR_SOME_INSTRUCTIONS, object, "S_ATTR_SOME_INSTRUCTIONS"); set_integer(S_ATTR_EXT_RELOC, object, "S_ATTR_EXT_RELOC"); set_integer(S_ATTR_LOC_RELOC, object, "S_ATTR_LOC_RELOC"); } // Get Mach-O file index in fat file by cputype field. define_function(file_index_type) { YR_OBJECT* module = module(); int64_t type_arg = integer_argument(1); uint64_t nfat = get_integer(module, "nfat_arch"); if (is_undefined(module, "nfat_arch")) return_integer(YR_UNDEFINED); for (int i = 0; i < nfat; i++) { int64_t type = get_integer(module, "file[%i].cputype", i); if (type == type_arg) { return_integer(i); } } return_integer(YR_UNDEFINED); } // Get Mach-O file index in fat file by cputype and cpusubtype fields. define_function(file_index_subtype) { YR_OBJECT* module = module(); int64_t type_arg = integer_argument(1); int64_t subtype_arg = integer_argument(2); uint64_t nfat = get_integer(module, "nfat_arch"); if (is_undefined(module, "nfat_arch")) return_integer(YR_UNDEFINED); for (int i = 0; i < nfat; i++) { int64_t type = get_integer(module, "file[%i].cputype", i); int64_t subtype = get_integer(module, "file[%i].cpusubtype", i); if (type == type_arg && subtype == subtype_arg) { return_integer(i); } } return_integer(YR_UNDEFINED); } // Get real entry point offset for specific architecture in fat Mach-O. define_function(ep_for_arch_type) { YR_OBJECT* module = module(); int64_t type_arg = integer_argument(1); uint64_t nfat = get_integer(module, "nfat_arch"); if (is_undefined(module, "nfat_arch")) return_integer(YR_UNDEFINED); for (int i = 0; i < nfat; i++) { int64_t type = get_integer(module, "fat_arch[%i].cputype", i); if (type == type_arg) { uint64_t file_offset = get_integer(module, "fat_arch[%i].offset", i); uint64_t entry_point = get_integer(module, "file[%i].entry_point", i); return_integer(file_offset + entry_point); } } return_integer(YR_UNDEFINED); } // Get real entry point offset for specific architecture in fat Mach-O. define_function(ep_for_arch_subtype) { YR_OBJECT* module = module(); int64_t type_arg = integer_argument(1); int64_t subtype_arg = integer_argument(2); uint64_t nfat = get_integer(module, "nfat_arch"); if (is_undefined(module, "nfat_arch")) return_integer(YR_UNDEFINED); for (int i = 0; i < nfat; i++) { int64_t type = get_integer(module, "fat_arch[%i].cputype", i); int64_t subtype = get_integer(module, "fat_arch[%i].cpusubtype", i); if (type == type_arg && subtype == subtype_arg) { uint64_t file_offset = get_integer(module, "fat_arch[%i].offset", i); uint64_t entry_point = get_integer(module, "file[%i].entry_point", i); return_integer(file_offset + entry_point); } } return_integer(YR_UNDEFINED); } begin_declarations // Magic constants declare_integer("MH_MAGIC"); declare_integer("MH_CIGAM"); declare_integer("MH_MAGIC_64"); declare_integer("MH_CIGAM_64"); // Fat magic constants declare_integer("FAT_MAGIC"); declare_integer("FAT_CIGAM"); declare_integer("FAT_MAGIC_64"); declare_integer("FAT_CIGAM_64"); // 64-bit masks declare_integer("CPU_ARCH_ABI64"); declare_integer("CPU_SUBTYPE_LIB64"); // CPU types declare_integer("CPU_TYPE_MC680X0"); declare_integer("CPU_TYPE_X86"); declare_integer("CPU_TYPE_I386"); declare_integer("CPU_TYPE_X86_64"); declare_integer("CPU_TYPE_MIPS"); declare_integer("CPU_TYPE_MC98000"); declare_integer("CPU_TYPE_ARM"); declare_integer("CPU_TYPE_ARM64"); declare_integer("CPU_TYPE_MC88000"); declare_integer("CPU_TYPE_SPARC"); declare_integer("CPU_TYPE_POWERPC"); declare_integer("CPU_TYPE_POWERPC64"); // CPU sub-types declare_integer("CPU_SUBTYPE_INTEL_MODEL_ALL"); declare_integer("CPU_SUBTYPE_386"); declare_integer("CPU_SUBTYPE_I386_ALL"); declare_integer("CPU_SUBTYPE_X86_64_ALL"); declare_integer("CPU_SUBTYPE_486"); declare_integer("CPU_SUBTYPE_486SX"); declare_integer("CPU_SUBTYPE_586"); declare_integer("CPU_SUBTYPE_PENT"); declare_integer("CPU_SUBTYPE_PENTPRO"); declare_integer("CPU_SUBTYPE_PENTII_M3"); declare_integer("CPU_SUBTYPE_PENTII_M5"); declare_integer("CPU_SUBTYPE_CELERON"); declare_integer("CPU_SUBTYPE_CELERON_MOBILE"); declare_integer("CPU_SUBTYPE_PENTIUM_3"); declare_integer("CPU_SUBTYPE_PENTIUM_3_M"); declare_integer("CPU_SUBTYPE_PENTIUM_3_XEON"); declare_integer("CPU_SUBTYPE_PENTIUM_M"); declare_integer("CPU_SUBTYPE_PENTIUM_4"); declare_integer("CPU_SUBTYPE_PENTIUM_4_M"); declare_integer("CPU_SUBTYPE_ITANIUM"); declare_integer("CPU_SUBTYPE_ITANIUM_2"); declare_integer("CPU_SUBTYPE_XEON"); declare_integer("CPU_SUBTYPE_XEON_MP"); declare_integer("CPU_SUBTYPE_ARM_ALL"); declare_integer("CPU_SUBTYPE_ARM_V4T"); declare_integer("CPU_SUBTYPE_ARM_V6"); declare_integer("CPU_SUBTYPE_ARM_V5"); declare_integer("CPU_SUBTYPE_ARM_V5TEJ"); declare_integer("CPU_SUBTYPE_ARM_XSCALE"); declare_integer("CPU_SUBTYPE_ARM_V7"); declare_integer("CPU_SUBTYPE_ARM_V7F"); declare_integer("CPU_SUBTYPE_ARM_V7S"); declare_integer("CPU_SUBTYPE_ARM_V7K"); declare_integer("CPU_SUBTYPE_ARM_V6M"); declare_integer("CPU_SUBTYPE_ARM_V7M"); declare_integer("CPU_SUBTYPE_ARM_V7EM"); declare_integer("CPU_SUBTYPE_ARM64_ALL"); declare_integer("CPU_SUBTYPE_SPARC_ALL"); declare_integer("CPU_SUBTYPE_POWERPC_ALL"); declare_integer("CPU_SUBTYPE_MC980000_ALL"); declare_integer("CPU_SUBTYPE_POWERPC_601"); declare_integer("CPU_SUBTYPE_MC98601"); declare_integer("CPU_SUBTYPE_POWERPC_602"); declare_integer("CPU_SUBTYPE_POWERPC_603"); declare_integer("CPU_SUBTYPE_POWERPC_603e"); declare_integer("CPU_SUBTYPE_POWERPC_603ev"); declare_integer("CPU_SUBTYPE_POWERPC_604"); declare_integer("CPU_SUBTYPE_POWERPC_604e"); declare_integer("CPU_SUBTYPE_POWERPC_620"); declare_integer("CPU_SUBTYPE_POWERPC_750"); declare_integer("CPU_SUBTYPE_POWERPC_7400"); declare_integer("CPU_SUBTYPE_POWERPC_7450"); declare_integer("CPU_SUBTYPE_POWERPC_970"); // File types declare_integer("MH_OBJECT"); declare_integer("MH_EXECUTE"); declare_integer("MH_FVMLIB"); declare_integer("MH_CORE"); declare_integer("MH_PRELOAD"); declare_integer("MH_DYLIB"); declare_integer("MH_DYLINKER"); declare_integer("MH_BUNDLE"); declare_integer("MH_DYLIB_STUB"); declare_integer("MH_DSYM"); declare_integer("MH_KEXT_BUNDLE"); // Header flags declare_integer("MH_NOUNDEFS"); declare_integer("MH_INCRLINK"); declare_integer("MH_DYLDLINK"); declare_integer("MH_BINDATLOAD"); declare_integer("MH_PREBOUND"); declare_integer("MH_SPLIT_SEGS"); declare_integer("MH_LAZY_INIT"); declare_integer("MH_TWOLEVEL"); declare_integer("MH_FORCE_FLAT"); declare_integer("MH_NOMULTIDEFS"); declare_integer("MH_NOFIXPREBINDING"); declare_integer("MH_PREBINDABLE"); declare_integer("MH_ALLMODSBOUND"); declare_integer("MH_SUBSECTIONS_VIA_SYMBOLS"); declare_integer("MH_CANONICAL"); declare_integer("MH_WEAK_DEFINES"); declare_integer("MH_BINDS_TO_WEAK"); declare_integer("MH_ALLOW_STACK_EXECUTION"); declare_integer("MH_ROOT_SAFE"); declare_integer("MH_SETUID_SAFE"); declare_integer("MH_NO_REEXPORTED_DYLIBS"); declare_integer("MH_PIE"); declare_integer("MH_DEAD_STRIPPABLE_DYLIB"); declare_integer("MH_HAS_TLV_DESCRIPTORS"); declare_integer("MH_NO_HEAP_EXECUTION"); declare_integer("MH_APP_EXTENSION_SAFE"); // Segment flags declare_integer("SG_HIGHVM"); declare_integer("SG_FVMLIB"); declare_integer("SG_NORELOC"); declare_integer("SG_PROTECTED_VERSION_1"); // Section masks declare_integer("SECTION_TYPE"); declare_integer("SECTION_ATTRIBUTES"); // Section types declare_integer("S_REGULAR"); declare_integer("S_ZEROFILL"); declare_integer("S_CSTRING_LITERALS"); declare_integer("S_4BYTE_LITERALS"); declare_integer("S_8BYTE_LITERALS"); declare_integer("S_LITERAL_POINTERS"); declare_integer("S_NON_LAZY_SYMBOL_POINTERS"); declare_integer("S_LAZY_SYMBOL_POINTERS"); declare_integer("S_SYMBOL_STUBS"); declare_integer("S_MOD_INIT_FUNC_POINTERS"); declare_integer("S_MOD_TERM_FUNC_POINTERS"); declare_integer("S_COALESCED"); declare_integer("S_GB_ZEROFILL"); declare_integer("S_INTERPOSING"); declare_integer("S_16BYTE_LITERALS"); declare_integer("S_DTRACE_DOF"); declare_integer("S_LAZY_DYLIB_SYMBOL_POINTERS"); declare_integer("S_THREAD_LOCAL_REGULAR"); declare_integer("S_THREAD_LOCAL_ZEROFILL"); declare_integer("S_THREAD_LOCAL_VARIABLES"); declare_integer("S_THREAD_LOCAL_VARIABLE_POINTERS"); declare_integer("S_THREAD_LOCAL_INIT_FUNCTION_POINTERS"); // Section attributes declare_integer("S_ATTR_PURE_INSTRUCTIONS"); declare_integer("S_ATTR_NO_TOC"); declare_integer("S_ATTR_STRIP_STATIC_SYMS"); declare_integer("S_ATTR_NO_DEAD_STRIP"); declare_integer("S_ATTR_LIVE_SUPPORT"); declare_integer("S_ATTR_SELF_MODIFYING_CODE"); declare_integer("S_ATTR_DEBUG"); declare_integer("S_ATTR_SOME_INSTRUCTIONS"); declare_integer("S_ATTR_EXT_RELOC"); declare_integer("S_ATTR_LOC_RELOC"); // Header declare_integer("magic"); declare_integer("cputype"); declare_integer("cpusubtype"); declare_integer("filetype"); declare_integer("ncmds"); declare_integer("sizeofcmds"); declare_integer("flags"); declare_integer("reserved"); // Segments and nested sections declare_integer("number_of_segments"); begin_struct_array("segments") declare_string("segname"); declare_integer("vmaddr"); declare_integer("vmsize"); declare_integer("fileoff"); declare_integer("fsize"); declare_integer("maxprot"); declare_integer("initprot"); declare_integer("nsects"); declare_integer("flags"); begin_struct_array("sections") declare_string("sectname"); declare_string("segname"); declare_integer("addr"); declare_integer("size"); declare_integer("offset"); declare_integer("align"); declare_integer("reloff"); declare_integer("nreloc"); declare_integer("flags"); declare_integer("reserved1"); declare_integer("reserved2"); declare_integer("reserved3"); end_struct_array("sections"); end_struct_array("segments") // Entry point and stack size declare_integer("entry_point"); declare_integer("stack_size"); // Mach-O fat binary header declare_integer("fat_magic"); declare_integer("nfat_arch"); begin_struct_array("fat_arch") declare_integer("cputype"); declare_integer("cpusubtype"); declare_integer("offset"); declare_integer("size"); declare_integer("align"); end_struct_array("fat_arch") // Included Mach-O files (must be same as single file structure above) begin_struct_array("file") // Single file header declare_integer("magic"); declare_integer("cputype"); declare_integer("cpusubtype"); declare_integer("filetype"); declare_integer("ncmds"); declare_integer("sizeofcmds"); declare_integer("flags"); declare_integer("reserved"); // Segments and nested sections declare_integer("number_of_segments"); begin_struct_array("segments") declare_string("segname"); declare_integer("vmaddr"); declare_integer("vmsize"); declare_integer("fileoff"); declare_integer("fsize"); declare_integer("maxprot"); declare_integer("initprot"); declare_integer("nsects"); declare_integer("flags"); begin_struct_array("sections") declare_string("sectname"); declare_string("segname"); declare_integer("addr"); declare_integer("size"); declare_integer("offset"); declare_integer("align"); declare_integer("reloff"); declare_integer("nreloc"); declare_integer("flags"); declare_integer("reserved1"); declare_integer("reserved2"); declare_integer("reserved3"); end_struct_array("sections"); end_struct_array("segments") // Entry point and stack size declare_integer("entry_point"); declare_integer("stack_size"); end_struct_array("file"); // Mach-O fat binary helper functions declare_function("file_index_for_arch", "i", "i", file_index_type); declare_function("file_index_for_arch", "ii", "i", file_index_subtype); declare_function("entry_point_for_arch", "i", "i", ep_for_arch_type); declare_function("entry_point_for_arch", "ii", "i", ep_for_arch_subtype); end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; foreach_memory_block(iterator, block) { const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL || block->size < 4) continue; // Parse Mach-O binary. if (is_macho_file_block((uint32_t*) block_data)) { macho_parse_file(block_data, block->size, module_object, context); break; } // Parse fat Mach-O binary. if (is_fat_macho_file_block((uint32_t*) block_data)) { macho_parse_fat_file(block_data, block->size, module_object, context); break; } } macho_set_definitions(module_object); return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/magic/000077500000000000000000000000001413423160300166235ustar00rootroot00000000000000yara-4.1.3/libyara/modules/magic/magic.c000066400000000000000000000115461413423160300200560ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* The original idea and inspiration for this module comes from Armin Buescher. */ #include #include #include #define MODULE_NAME magic // Thread-local storage key used to store a pointer to a MAGIC_CACHE struct. YR_THREAD_STORAGE_KEY magic_tls; typedef struct { magic_t magic_cookie; const char* cached_type; const char* cached_mime_type; } MAGIC_CACHE; static int get_cache(MAGIC_CACHE** cache) { *cache = (MAGIC_CACHE*) yr_thread_storage_get_value(&magic_tls); if (*cache == NULL) { *cache = (MAGIC_CACHE*) yr_malloc(sizeof(MAGIC_CACHE)); if (*cache == NULL) return ERROR_INSUFFICIENT_MEMORY; (*cache)->magic_cookie = magic_open(0); if ((*cache)->magic_cookie == NULL) return ERROR_INSUFFICIENT_MEMORY; if (magic_load((*cache)->magic_cookie, NULL) != 0) { magic_close((*cache)->magic_cookie); return ERROR_INTERNAL_FATAL_ERROR; } (*cache)->cached_type = NULL; (*cache)->cached_mime_type = NULL; return yr_thread_storage_set_value(&magic_tls, *cache); } return ERROR_SUCCESS; } define_function(magic_mime_type) { YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block; MAGIC_CACHE* cache; const uint8_t* block_data; if (context->flags & SCAN_FLAGS_PROCESS_MEMORY) return_string(YR_UNDEFINED); FAIL_ON_ERROR(get_cache(&cache)); if (cache->cached_mime_type == NULL) { block = first_memory_block(context); block_data = block->fetch_data(block); if (block_data != NULL) { magic_setflags(cache->magic_cookie, MAGIC_MIME_TYPE); cache->cached_mime_type = magic_buffer( cache->magic_cookie, block_data, block->size); } } if (cache->cached_mime_type == NULL) return_string(YR_UNDEFINED); return_string((char*) cache->cached_mime_type); } define_function(magic_type) { MAGIC_CACHE* cache; YR_MEMORY_BLOCK* block; YR_SCAN_CONTEXT* context = scan_context(); const uint8_t* block_data; if (context->flags & SCAN_FLAGS_PROCESS_MEMORY) return_string(YR_UNDEFINED); FAIL_ON_ERROR(get_cache(&cache)); if (cache->cached_type == NULL) { block = first_memory_block(context); block_data = block->fetch_data(block); if (block_data != NULL) { magic_setflags(cache->magic_cookie, 0); cache->cached_type = magic_buffer( cache->magic_cookie, block_data, block->size); } } if (cache->cached_type == NULL) return_string(YR_UNDEFINED); return_string((char*) cache->cached_type); } begin_declarations declare_function("mime_type", "", "s", magic_mime_type); declare_function("type", "", "s", magic_type); end_declarations int module_initialize(YR_MODULE* module) { return yr_thread_storage_create(&magic_tls); } int module_finalize(YR_MODULE* module) { MAGIC_CACHE* cache = (MAGIC_CACHE*) yr_thread_storage_get_value(&magic_tls); if (cache != NULL) { magic_close(cache->magic_cookie); yr_free(cache); } return yr_thread_storage_destroy(&magic_tls); } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module) { MAGIC_CACHE* cache = (MAGIC_CACHE*) yr_thread_storage_get_value(&magic_tls); if (cache != NULL) { cache->cached_type = NULL; cache->cached_mime_type = NULL; } return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/math/000077500000000000000000000000001413423160300164745ustar00rootroot00000000000000yara-4.1.3/libyara/modules/math/math.c000066400000000000000000000363021413423160300175750ustar00rootroot00000000000000/* Copyright (c) 2014-2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #define MODULE_NAME math #define PI 3.141592653589793 // log2 is not defined by math.h in VC++ #if defined(_MSC_VER) && _MSC_VER < 1800 static double log2(double n) { return log(n) / log(2.0); } #endif define_function(string_entropy) { size_t i; double entropy = 0.0; SIZED_STRING* s = sized_string_argument(1); uint32_t* data = (uint32_t*) yr_calloc(256, sizeof(uint32_t)); if (data == NULL) return_float(YR_UNDEFINED); for (i = 0; i < s->length; i++) { uint8_t c = s->c_string[i]; data[c] += 1; } for (i = 0; i < 256; i++) { if (data[i] != 0) { double x = (double) (data[i]) / s->length; entropy -= x * log2(x); } } yr_free(data); return_float(entropy); } define_function(data_entropy) { bool past_first_block = false; double entropy = 0.0; size_t total_len = 0; size_t i; uint32_t* data; int64_t offset = integer_argument(1); // offset where to start int64_t length = integer_argument(2); // length of bytes we want entropy on YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; if (offset < 0 || length < 0 || offset < block->base) return_float(YR_UNDEFINED); data = (uint32_t*) yr_calloc(256, sizeof(uint32_t)); if (data == NULL) return_float(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min( length, (size_t)(block->size - data_offset)); const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL) { yr_free(data); return_float(YR_UNDEFINED); } total_len += data_len; offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) { uint8_t c = *(block_data + data_offset + i); data[c] += 1; } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. yr_free(data); return_float(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) { yr_free(data); return_float(YR_UNDEFINED); } for (i = 0; i < 256; i++) { if (data[i] != 0) { double x = (double) (data[i]) / total_len; entropy -= x * log2(x); } } yr_free(data); return_float(entropy); } define_function(string_deviation) { SIZED_STRING* s = sized_string_argument(1); double mean = float_argument(2); double sum = 0.0; size_t i; for (i = 0; i < s->length; i++) sum += fabs(((double) s->c_string[i]) - mean); return_float(sum / s->length); } define_function(data_deviation) { int past_first_block = false; int64_t offset = integer_argument(1); int64_t length = integer_argument(2); double mean = float_argument(3); double sum = 0.0; size_t total_len = 0; size_t i; size_t data_offset = 0; size_t data_len = 0; const uint8_t* block_data = NULL; YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; if (offset < 0 || length < 0 || offset < block->base) return_float(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { data_offset = (size_t)(offset - block->base); data_len = (size_t) yr_min(length, (size_t)(block->size - data_offset)); block_data = block->fetch_data(block); if (block_data == NULL) return_float(YR_UNDEFINED); total_len += data_len; offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) sum += fabs(((double) *(block_data + data_offset + i)) - mean); past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. return_float(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) return_float(YR_UNDEFINED); return_float(sum / total_len); } define_function(string_mean) { size_t i; double sum = 0.0; SIZED_STRING* s = sized_string_argument(1); for (i = 0; i < s->length; i++) sum += (double) s->c_string[i]; return_float(sum / s->length); } define_function(data_mean) { int past_first_block = false; double sum = 0.0; int64_t offset = integer_argument(1); int64_t length = integer_argument(2); YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; size_t total_len = 0; size_t i; if (offset < 0 || length < 0 || offset < block->base) return_float(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min( length, (size_t)(block->size - data_offset)); const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL) return_float(YR_UNDEFINED); total_len += data_len; offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) sum += (double) *(block_data + data_offset + i); past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. return_float(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) return_float(YR_UNDEFINED); return_float(sum / total_len); } define_function(data_serial_correlation) { int past_first_block = false; size_t total_len = 0; size_t i; int64_t offset = integer_argument(1); int64_t length = integer_argument(2); YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; double sccun = 0; double scclast = 0; double scct1 = 0; double scct2 = 0; double scct3 = 0; double scc = 0; if (offset < 0 || length < 0 || offset < block->base) return_float(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min( length, (size_t)(block->size - data_offset)); const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL) return_float(YR_UNDEFINED); total_len += data_len; offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) { sccun = (double) *(block_data + data_offset + i); scct1 += scclast * sccun; scct2 += sccun; scct3 += sccun * sccun; scclast = sccun; } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. return_float(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block) return_float(YR_UNDEFINED); scct1 += scclast * sccun; scct2 *= scct2; scc = total_len * scct3 - scct2; if (scc == 0) scc = -100000; else scc = (total_len * scct1 - scct2) / scc; return_float(scc); } define_function(string_serial_correlation) { SIZED_STRING* s = sized_string_argument(1); double sccun = 0; double scclast = 0; double scct1 = 0; double scct2 = 0; double scct3 = 0; double scc = 0; size_t i; for (i = 0; i < s->length; i++) { sccun = (double) s->c_string[i]; scct1 += scclast * sccun; scct2 += sccun; scct3 += sccun * sccun; scclast = sccun; } scct1 += scclast * sccun; scct2 *= scct2; scc = s->length * scct3 - scct2; if (scc == 0) scc = -100000; else scc = (s->length * scct1 - scct2) / scc; return_float(scc); } define_function(data_monte_carlo_pi) { int past_first_block = false; int mcount = 0; int inmont = 0; double INCIRC = pow(pow(256.0, 3.0) - 1, 2.0); double mpi = 0; size_t i; int64_t offset = integer_argument(1); int64_t length = integer_argument(2); YR_SCAN_CONTEXT* context = scan_context(); YR_MEMORY_BLOCK* block = first_memory_block(context); YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; if (offset < 0 || length < 0 || offset < block->base) return_float(YR_UNDEFINED); foreach_memory_block(iterator, block) { if (offset >= block->base && offset < block->base + block->size) { unsigned int monte[6]; size_t data_offset = (size_t)(offset - block->base); size_t data_len = (size_t) yr_min( length, (size_t)(block->size - data_offset)); const uint8_t* block_data = block->fetch_data(block); if (block_data == NULL) return_float(YR_UNDEFINED); offset += data_len; length -= data_len; for (i = 0; i < data_len; i++) { monte[i % 6] = (unsigned int) *(block_data + data_offset + i); if (i % 6 == 5) { double mx = 0; double my = 0; int j; mcount++; for (j = 0; j < 3; j++) { mx = (mx * 256.0) + monte[j]; my = (my * 256.0) + monte[j + 3]; } if ((mx * mx + my * my) <= INCIRC) inmont++; } } past_first_block = true; } else if (past_first_block) { // If offset is not within current block and we already // past the first block then the we are trying to compute // the checksum over a range of non contiguous blocks. As // range contains gaps of undefined data the checksum is // undefined. return_float(YR_UNDEFINED); } if (block->base + block->size > offset + length) break; } if (!past_first_block || mcount == 0) return_float(YR_UNDEFINED); mpi = 4.0 * ((double) inmont / mcount); return_float(fabs((mpi - PI) / PI)); } define_function(string_monte_carlo_pi) { SIZED_STRING* s = sized_string_argument(1); double INCIRC = pow(pow(256.0, 3.0) - 1, 2.0); double mpi = 0; unsigned int monte[6]; int mcount = 0; int inmont = 0; size_t i; for (i = 0; i < s->length; i++) { monte[i % 6] = (unsigned int) s->c_string[i]; if (i % 6 == 5) { double mx = 0; double my = 0; int j; mcount++; for (j = 0; j < 3; j++) { mx = (mx * 256.0) + monte[j]; my = (my * 256.0) + monte[j + 3]; } if ((mx * mx + my * my) <= INCIRC) inmont++; } } if (mcount == 0) return_float(YR_UNDEFINED); mpi = 4.0 * ((double) inmont / mcount); return_float(fabs((mpi - PI) / PI)); } define_function(in_range) { double test = float_argument(1); double lower = float_argument(2); double upper = float_argument(3); return_integer((lower <= test && test <= upper) ? 1 : 0); } // Undefine existing "min" and "max" macros in order to avoid conflicts with // function names. #undef min #undef max define_function(min) { uint64_t i = integer_argument(1); uint64_t j = integer_argument(2); return_integer(i < j ? i : j); } define_function(max) { uint64_t i = integer_argument(1); uint64_t j = integer_argument(2); return_integer(i > j ? i : j); } define_function(to_number) { return_integer(integer_argument(1) ? 1 : 0); } begin_declarations declare_float("MEAN_BYTES"); declare_function("in_range", "fff", "i", in_range); declare_function("deviation", "iif", "f", data_deviation); declare_function("deviation", "sf", "f", string_deviation); declare_function("mean", "ii", "f", data_mean); declare_function("mean", "s", "f", string_mean); declare_function("serial_correlation", "ii", "f", data_serial_correlation); declare_function("serial_correlation", "s", "f", string_serial_correlation); declare_function("monte_carlo_pi", "ii", "f", data_monte_carlo_pi); declare_function("monte_carlo_pi", "s", "f", string_monte_carlo_pi); declare_function("entropy", "ii", "f", data_entropy); declare_function("entropy", "s", "f", string_entropy); declare_function("min", "ii", "i", min); declare_function("max", "ii", "i", max); declare_function("to_number", "b", "i", to_number); end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { set_float(127.5, module_object, "MEAN_BYTES"); return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/module_list000066400000000000000000000005511413423160300200070ustar00rootroot00000000000000MODULE(tests) MODULE(pe) MODULE(elf) MODULE(math) MODULE(time) #ifdef DOTNET_MODULE MODULE(dotnet) #endif #ifdef CUCKOO_MODULE MODULE(cuckoo) #endif #ifdef MAGIC_MODULE MODULE(magic) #endif #ifdef HASH_MODULE MODULE(hash) #endif #ifdef MACHO_MODULE MODULE(macho) #endif #ifdef DEX_MODULE MODULE(dex) #endif #ifdef PB_TESTS_MODULE MODULE(pb_tests) #endif yara-4.1.3/libyara/modules/pb_tests/000077500000000000000000000000001413423160300173665ustar00rootroot00000000000000yara-4.1.3/libyara/modules/pb_tests/pb_tests.c000066400000000000000000000170351413423160300213630ustar00rootroot00000000000000 /* Generated by the protoc-gen-yara. DO NOT EDIT! */ #include #include #include "modules/pb_tests/pb_tests.pb-c.h" #define MODULE_NAME pb_tests static void* _pb_alloc(void* allocator_data, size_t size) { return yr_malloc(size); } static void _pb_free(void* allocator_data, void* pointer) { return yr_free(pointer); } begin_declarations begin_struct("struct") begin_struct("enum") declare_integer("FIRST"); declare_integer("SECOND"); end_struct("enum"); end_struct("struct"); declare_integer("f_int32"); declare_integer("f_int64"); declare_integer("f_sint32"); declare_integer("f_sint64"); declare_integer("f_sfixed32"); declare_integer("f_sfixed64"); declare_integer("f_bool"); declare_string("f_string"); declare_string("f_bytes"); begin_struct_array("f_struct_array") declare_string("f_string"); declare_integer("f_enum"); begin_struct("f_nested_struct") declare_integer("f_int32"); declare_string("f_string"); end_struct("f_nested_struct") begin_struct_array("f_nested_struct_array") declare_integer("f_int32"); declare_string("f_string"); end_struct_array("f_nested_struct_array"); end_struct_array("f_struct_array") declare_integer_dictionary("f_map_int32"); declare_integer_dictionary("f_map_bool"); declare_string_dictionary("f_map_string"); declare_float_dictionary("f_map_float"); begin_struct_dictionary("f_map_struct") declare_integer("f_int32"); declare_integer("f_int64"); end_struct_dictionary("f_map_struct") declare_string("f_oneof_string"); begin_struct("f_oneof_struct") declare_integer("f_int32"); declare_integer("f_int64"); end_struct("f_oneof_struct") declare_string("f_yara_name"); end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { ProtobufCAllocator allocator; allocator.alloc = _pb_alloc; allocator.free = _pb_free; if (module_data == NULL) return ERROR_SUCCESS; Test__RootMessage* pb = test__root_message__unpack( &allocator, module_data_size, module_data); if (pb == NULL) return ERROR_INVALID_MODULE_DATA; set_integer(0, module_object, "struct.enum.FIRST"); set_integer(1, module_object, "struct.enum.SECOND"); if (pb->has_f_int32) { set_integer(pb->f_int32, module_object, "f_int32"); } if (pb->has_f_int64) { set_integer(pb->f_int64, module_object, "f_int64"); } if (pb->has_f_sint32) { set_integer(pb->f_sint32, module_object, "f_sint32"); } if (pb->has_f_sint64) { set_integer(pb->f_sint64, module_object, "f_sint64"); } if (pb->has_f_sfixed32) { set_integer(pb->f_sfixed32, module_object, "f_sfixed32"); } if (pb->has_f_sfixed64) { set_integer(pb->f_sfixed64, module_object, "f_sfixed64"); } if (pb->has_f_bool) { set_integer(pb->f_bool, module_object, "f_bool"); } set_string(pb->f_string, module_object, "f_string"); set_sized_string( (const char*) pb->f_bytes.data, pb->f_bytes.len, module_object, "f_bytes"); for (int i = 0; i < pb->n_f_struct_array; i++) { if (pb->f_struct_array[i] != NULL) { set_string( pb->f_struct_array[i]->f_string, module_object, "f_struct_array[%i].f_string", i); if (pb->f_struct_array[i]->has_f_enum) { set_integer( pb->f_struct_array[i]->f_enum, module_object, "f_struct_array[%i].f_enum", i); } if (pb->f_struct_array[i]->f_nested_struct != NULL) { if (pb->f_struct_array[i]->f_nested_struct->has_f_int32) { set_integer( pb->f_struct_array[i]->f_nested_struct->f_int32, module_object, "f_struct_array[%i].f_nested_struct.f_int32", i); } set_string( pb->f_struct_array[i]->f_nested_struct->f_string, module_object, "f_struct_array[%i].f_nested_struct.f_string", i); } for (int j = 0; j < pb->f_struct_array[i]->n_f_nested_struct_array; j++) { if (pb->f_struct_array[i]->f_nested_struct_array[j] != NULL) { if (pb->f_struct_array[i]->f_nested_struct_array[j]->has_f_int32) { set_integer( pb->f_struct_array[i]->f_nested_struct_array[j]->f_int32, module_object, "f_struct_array[%i].f_nested_struct_array[%i].f_int32", i, j); } set_string( pb->f_struct_array[i]->f_nested_struct_array[j]->f_string, module_object, "f_struct_array[%i].f_nested_struct_array[%i].f_string", i, j); } } } } for (int i = 0; i < pb->n_f_map_int32; i++) { if (pb->f_map_int32[i] != NULL) { if (pb->f_map_int32[i]->has_value) { set_integer( pb->f_map_int32[i]->value, module_object, "f_map_int32[%s]", pb->f_map_int32[i]->key); } } } for (int i = 0; i < pb->n_f_map_bool; i++) { if (pb->f_map_bool[i] != NULL) { if (pb->f_map_bool[i]->has_value) { set_integer( pb->f_map_bool[i]->value, module_object, "f_map_bool[%s]", pb->f_map_bool[i]->key); } } } for (int i = 0; i < pb->n_f_map_string; i++) { if (pb->f_map_string[i] != NULL) { set_string( pb->f_map_string[i]->value, module_object, "f_map_string[%s]", pb->f_map_string[i]->key); } } for (int i = 0; i < pb->n_f_map_float; i++) { if (pb->f_map_float[i] != NULL) { if (pb->f_map_float[i]->has_value) { set_float( pb->f_map_float[i]->value, module_object, "f_map_float[%s]", pb->f_map_float[i]->key); } } } for (int i = 0; i < pb->n_f_map_struct; i++) { if (pb->f_map_struct[i] != NULL) { if (pb->f_map_struct[i]->value != NULL) { if (pb->f_map_struct[i]->value->has_f_int32) { set_integer( pb->f_map_struct[i]->value->f_int32, module_object, "f_map_struct[%s].f_int32", pb->f_map_struct[i]->key); } if (pb->f_map_struct[i]->value->has_f_int64) { set_integer( pb->f_map_struct[i]->value->f_int64, module_object, "f_map_struct[%s].f_int64", pb->f_map_struct[i]->key); } } } } if (pb->f_oneof_case == 20) { set_string(pb->f_oneof_string, module_object, "f_oneof_string"); } if (pb->f_oneof_case == 21) { if (pb->f_oneof_struct != NULL) { if (pb->f_oneof_struct->has_f_int32) { set_integer( pb->f_oneof_struct->f_int32, module_object, "f_oneof_struct.f_int32"); } if (pb->f_oneof_struct->has_f_int64) { set_integer( pb->f_oneof_struct->f_int64, module_object, "f_oneof_struct.f_int64"); } } } set_string(pb->f_renamed, module_object, "f_yara_name"); test__root_message__free_unpacked(pb, &allocator); return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/pb_tests/pb_tests.pb-c.c000066400000000000000000000732341413423160300222060ustar00rootroot00000000000000/* Generated by the protocol buffer compiler. DO NOT EDIT! */ /* Generated from: modules/pb_tests/pb_tests.proto */ /* Do not generate deprecated warnings for self */ #ifndef PROTOBUF_C__NO_DEPRECATED #define PROTOBUF_C__NO_DEPRECATED #endif #include "modules/pb_tests/pb_tests.pb-c.h" void test__struct__nested_struct__init(Test__Struct__NestedStruct *message) { static const Test__Struct__NestedStruct init_value = TEST__STRUCT__NESTED_STRUCT__INIT; *message = init_value; } void test__struct__init(Test__Struct *message) { static const Test__Struct init_value = TEST__STRUCT__INIT; *message = init_value; } size_t test__struct__get_packed_size(const Test__Struct *message) { assert(message->base.descriptor == &test__struct__descriptor); return protobuf_c_message_get_packed_size( (const ProtobufCMessage *) (message)); } size_t test__struct__pack(const Test__Struct *message, uint8_t *out) { assert(message->base.descriptor == &test__struct__descriptor); return protobuf_c_message_pack((const ProtobufCMessage *) message, out); } size_t test__struct__pack_to_buffer( const Test__Struct *message, ProtobufCBuffer *buffer) { assert(message->base.descriptor == &test__struct__descriptor); return protobuf_c_message_pack_to_buffer( (const ProtobufCMessage *) message, buffer); } Test__Struct *test__struct__unpack( ProtobufCAllocator *allocator, size_t len, const uint8_t *data) { return (Test__Struct *) protobuf_c_message_unpack( &test__struct__descriptor, allocator, len, data); } void test__struct__free_unpacked( Test__Struct *message, ProtobufCAllocator *allocator) { if (!message) return; assert(message->base.descriptor == &test__struct__descriptor); protobuf_c_message_free_unpacked((ProtobufCMessage *) message, allocator); } void test__map_struct__init(Test__MapStruct *message) { static const Test__MapStruct init_value = TEST__MAP_STRUCT__INIT; *message = init_value; } size_t test__map_struct__get_packed_size(const Test__MapStruct *message) { assert(message->base.descriptor == &test__map_struct__descriptor); return protobuf_c_message_get_packed_size( (const ProtobufCMessage *) (message)); } size_t test__map_struct__pack(const Test__MapStruct *message, uint8_t *out) { assert(message->base.descriptor == &test__map_struct__descriptor); return protobuf_c_message_pack((const ProtobufCMessage *) message, out); } size_t test__map_struct__pack_to_buffer( const Test__MapStruct *message, ProtobufCBuffer *buffer) { assert(message->base.descriptor == &test__map_struct__descriptor); return protobuf_c_message_pack_to_buffer( (const ProtobufCMessage *) message, buffer); } Test__MapStruct *test__map_struct__unpack( ProtobufCAllocator *allocator, size_t len, const uint8_t *data) { return (Test__MapStruct *) protobuf_c_message_unpack( &test__map_struct__descriptor, allocator, len, data); } void test__map_struct__free_unpacked( Test__MapStruct *message, ProtobufCAllocator *allocator) { if (!message) return; assert(message->base.descriptor == &test__map_struct__descriptor); protobuf_c_message_free_unpacked((ProtobufCMessage *) message, allocator); } void test__root_message__fmap_int32_entry__init( Test__RootMessage__FMapInt32Entry *message) { static const Test__RootMessage__FMapInt32Entry init_value = TEST__ROOT_MESSAGE__FMAP_INT32_ENTRY__INIT; *message = init_value; } void test__root_message__fmap_bool_entry__init( Test__RootMessage__FMapBoolEntry *message) { static const Test__RootMessage__FMapBoolEntry init_value = TEST__ROOT_MESSAGE__FMAP_BOOL_ENTRY__INIT; *message = init_value; } void test__root_message__fmap_string_entry__init( Test__RootMessage__FMapStringEntry *message) { static const Test__RootMessage__FMapStringEntry init_value = TEST__ROOT_MESSAGE__FMAP_STRING_ENTRY__INIT; *message = init_value; } void test__root_message__fmap_float_entry__init( Test__RootMessage__FMapFloatEntry *message) { static const Test__RootMessage__FMapFloatEntry init_value = TEST__ROOT_MESSAGE__FMAP_FLOAT_ENTRY__INIT; *message = init_value; } void test__root_message__fmap_struct_entry__init( Test__RootMessage__FMapStructEntry *message) { static const Test__RootMessage__FMapStructEntry init_value = TEST__ROOT_MESSAGE__FMAP_STRUCT_ENTRY__INIT; *message = init_value; } void test__root_message__init(Test__RootMessage *message) { static const Test__RootMessage init_value = TEST__ROOT_MESSAGE__INIT; *message = init_value; } size_t test__root_message__get_packed_size(const Test__RootMessage *message) { assert(message->base.descriptor == &test__root_message__descriptor); return protobuf_c_message_get_packed_size( (const ProtobufCMessage *) (message)); } size_t test__root_message__pack(const Test__RootMessage *message, uint8_t *out) { assert(message->base.descriptor == &test__root_message__descriptor); return protobuf_c_message_pack((const ProtobufCMessage *) message, out); } size_t test__root_message__pack_to_buffer( const Test__RootMessage *message, ProtobufCBuffer *buffer) { assert(message->base.descriptor == &test__root_message__descriptor); return protobuf_c_message_pack_to_buffer( (const ProtobufCMessage *) message, buffer); } Test__RootMessage *test__root_message__unpack( ProtobufCAllocator *allocator, size_t len, const uint8_t *data) { return (Test__RootMessage *) protobuf_c_message_unpack( &test__root_message__descriptor, allocator, len, data); } void test__root_message__free_unpacked( Test__RootMessage *message, ProtobufCAllocator *allocator) { if (!message) return; assert(message->base.descriptor == &test__root_message__descriptor); protobuf_c_message_free_unpacked((ProtobufCMessage *) message, allocator); } static const ProtobufCFieldDescriptor test__struct__nested_struct__field_descriptors[2] = { { "f_int32", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_INT32, offsetof(Test__Struct__NestedStruct, has_f_int32), offsetof(Test__Struct__NestedStruct, f_int32), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_string", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__Struct__NestedStruct, f_string), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__struct__nested_struct__field_indices_by_name[] = { 0, /* field[0] = f_int32 */ 1, /* field[1] = f_string */ }; static const ProtobufCIntRange test__struct__nested_struct__number_ranges[1 + 1] = {{1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__struct__nested_struct__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.Struct.NestedStruct", "NestedStruct", "Test__Struct__NestedStruct", "test", sizeof(Test__Struct__NestedStruct), 2, test__struct__nested_struct__field_descriptors, test__struct__nested_struct__field_indices_by_name, 1, test__struct__nested_struct__number_ranges, (ProtobufCMessageInit) test__struct__nested_struct__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCEnumValue test__struct__enum__enum_values_by_number[2] = { {"FIRST", "TEST__STRUCT__ENUM__FIRST", 0}, {"SECOND", "TEST__STRUCT__ENUM__SECOND", 1}, }; static const ProtobufCIntRange test__struct__enum__value_ranges[] = { {0, 0}, {0, 2}}; static const ProtobufCEnumValueIndex test__struct__enum__enum_values_by_name[2] = { {"FIRST", 0}, {"SECOND", 1}, }; const ProtobufCEnumDescriptor test__struct__enum__descriptor = { PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC, "test.Struct.Enum", "Enum", "Test__Struct__Enum", "test", 2, test__struct__enum__enum_values_by_number, 2, test__struct__enum__enum_values_by_name, 1, test__struct__enum__value_ranges, NULL, NULL, NULL, NULL /* reserved[1234] */ }; static const ProtobufCFieldDescriptor test__struct__field_descriptors[4] = { { "f_string", 1, PROTOBUF_C_LABEL_REQUIRED, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__Struct, f_string), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_enum", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_ENUM, offsetof(Test__Struct, has_f_enum), offsetof(Test__Struct, f_enum), &test__struct__enum__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_nested_struct", 3, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_MESSAGE, 0, /* quantifier_offset */ offsetof(Test__Struct, f_nested_struct), &test__struct__nested_struct__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_nested_struct_array", 4, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__Struct, n_f_nested_struct_array), offsetof(Test__Struct, f_nested_struct_array), &test__struct__nested_struct__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__struct__field_indices_by_name[] = { 1, /* field[1] = f_enum */ 2, /* field[2] = f_nested_struct */ 3, /* field[3] = f_nested_struct_array */ 0, /* field[0] = f_string */ }; static const ProtobufCIntRange test__struct__number_ranges[1 + 1] = { {1, 0}, {0, 4}}; const ProtobufCMessageDescriptor test__struct__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.Struct", "Struct", "Test__Struct", "test", sizeof(Test__Struct), 4, test__struct__field_descriptors, test__struct__field_indices_by_name, 1, test__struct__number_ranges, (ProtobufCMessageInit) test__struct__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__map_struct__field_descriptors[2] = { { "f_int32", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_INT32, offsetof(Test__MapStruct, has_f_int32), offsetof(Test__MapStruct, f_int32), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_int64", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_INT64, offsetof(Test__MapStruct, has_f_int64), offsetof(Test__MapStruct, f_int64), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__map_struct__field_indices_by_name[] = { 0, /* field[0] = f_int32 */ 1, /* field[1] = f_int64 */ }; static const ProtobufCIntRange test__map_struct__number_ranges[1 + 1] = { {1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__map_struct__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.MapStruct", "MapStruct", "Test__MapStruct", "test", sizeof(Test__MapStruct), 2, test__map_struct__field_descriptors, test__map_struct__field_indices_by_name, 1, test__map_struct__number_ranges, (ProtobufCMessageInit) test__map_struct__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__root_message__fmap_int32_entry__field_descriptors[2] = { { "key", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapInt32Entry, key), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "value", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_INT32, offsetof(Test__RootMessage__FMapInt32Entry, has_value), offsetof(Test__RootMessage__FMapInt32Entry, value), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__root_message__fmap_int32_entry__field_indices_by_name[] = { 0, /* field[0] = key */ 1, /* field[1] = value */ }; static const ProtobufCIntRange test__root_message__fmap_int32_entry__number_ranges[1 + 1] = { {1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__root_message__fmap_int32_entry__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.RootMessage.FMapInt32Entry", "FMapInt32Entry", "Test__RootMessage__FMapInt32Entry", "test", sizeof(Test__RootMessage__FMapInt32Entry), 2, test__root_message__fmap_int32_entry__field_descriptors, test__root_message__fmap_int32_entry__field_indices_by_name, 1, test__root_message__fmap_int32_entry__number_ranges, (ProtobufCMessageInit) test__root_message__fmap_int32_entry__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__root_message__fmap_bool_entry__field_descriptors[2] = { { "key", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapBoolEntry, key), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "value", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_BOOL, offsetof(Test__RootMessage__FMapBoolEntry, has_value), offsetof(Test__RootMessage__FMapBoolEntry, value), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__root_message__fmap_bool_entry__field_indices_by_name[] = { 0, /* field[0] = key */ 1, /* field[1] = value */ }; static const ProtobufCIntRange test__root_message__fmap_bool_entry__number_ranges[1 + 1] = { {1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__root_message__fmap_bool_entry__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.RootMessage.FMapBoolEntry", "FMapBoolEntry", "Test__RootMessage__FMapBoolEntry", "test", sizeof(Test__RootMessage__FMapBoolEntry), 2, test__root_message__fmap_bool_entry__field_descriptors, test__root_message__fmap_bool_entry__field_indices_by_name, 1, test__root_message__fmap_bool_entry__number_ranges, (ProtobufCMessageInit) test__root_message__fmap_bool_entry__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__root_message__fmap_string_entry__field_descriptors[2] = { { "key", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapStringEntry, key), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "value", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapStringEntry, value), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__root_message__fmap_string_entry__field_indices_by_name[] = { 0, /* field[0] = key */ 1, /* field[1] = value */ }; static const ProtobufCIntRange test__root_message__fmap_string_entry__number_ranges[1 + 1] = { {1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__root_message__fmap_string_entry__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.RootMessage.FMapStringEntry", "FMapStringEntry", "Test__RootMessage__FMapStringEntry", "test", sizeof(Test__RootMessage__FMapStringEntry), 2, test__root_message__fmap_string_entry__field_descriptors, test__root_message__fmap_string_entry__field_indices_by_name, 1, test__root_message__fmap_string_entry__number_ranges, (ProtobufCMessageInit) test__root_message__fmap_string_entry__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__root_message__fmap_float_entry__field_descriptors[2] = { { "key", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapFloatEntry, key), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "value", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_FLOAT, offsetof(Test__RootMessage__FMapFloatEntry, has_value), offsetof(Test__RootMessage__FMapFloatEntry, value), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__root_message__fmap_float_entry__field_indices_by_name[] = { 0, /* field[0] = key */ 1, /* field[1] = value */ }; static const ProtobufCIntRange test__root_message__fmap_float_entry__number_ranges[1 + 1] = { {1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__root_message__fmap_float_entry__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.RootMessage.FMapFloatEntry", "FMapFloatEntry", "Test__RootMessage__FMapFloatEntry", "test", sizeof(Test__RootMessage__FMapFloatEntry), 2, test__root_message__fmap_float_entry__field_descriptors, test__root_message__fmap_float_entry__field_indices_by_name, 1, test__root_message__fmap_float_entry__number_ranges, (ProtobufCMessageInit) test__root_message__fmap_float_entry__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__root_message__fmap_struct_entry__field_descriptors[2] = { { "key", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapStructEntry, key), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "value", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_MESSAGE, 0, /* quantifier_offset */ offsetof(Test__RootMessage__FMapStructEntry, value), &test__map_struct__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__root_message__fmap_struct_entry__field_indices_by_name[] = { 0, /* field[0] = key */ 1, /* field[1] = value */ }; static const ProtobufCIntRange test__root_message__fmap_struct_entry__number_ranges[1 + 1] = { {1, 0}, {0, 2}}; const ProtobufCMessageDescriptor test__root_message__fmap_struct_entry__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.RootMessage.FMapStructEntry", "FMapStructEntry", "Test__RootMessage__FMapStructEntry", "test", sizeof(Test__RootMessage__FMapStructEntry), 2, test__root_message__fmap_struct_entry__field_descriptors, test__root_message__fmap_struct_entry__field_indices_by_name, 1, test__root_message__fmap_struct_entry__number_ranges, (ProtobufCMessageInit) test__root_message__fmap_struct_entry__init, NULL, NULL, NULL /* reserved[123] */ }; static const ProtobufCFieldDescriptor test__root_message__field_descriptors[19] = { { "f_int32", 1, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_INT32, offsetof(Test__RootMessage, has_f_int32), offsetof(Test__RootMessage, f_int32), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_int64", 2, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_INT64, offsetof(Test__RootMessage, has_f_int64), offsetof(Test__RootMessage, f_int64), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_sint32", 5, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_SINT32, offsetof(Test__RootMessage, has_f_sint32), offsetof(Test__RootMessage, f_sint32), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_sint64", 6, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_SINT64, offsetof(Test__RootMessage, has_f_sint64), offsetof(Test__RootMessage, f_sint64), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_sfixed32", 9, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_SFIXED32, offsetof(Test__RootMessage, has_f_sfixed32), offsetof(Test__RootMessage, f_sfixed32), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_sfixed64", 10, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_SFIXED64, offsetof(Test__RootMessage, has_f_sfixed64), offsetof(Test__RootMessage, f_sfixed64), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_bool", 11, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_BOOL, offsetof(Test__RootMessage, has_f_bool), offsetof(Test__RootMessage, f_bool), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_string", 12, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage, f_string), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_bytes", 13, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_BYTES, offsetof(Test__RootMessage, has_f_bytes), offsetof(Test__RootMessage, f_bytes), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_struct_array", 14, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, n_f_struct_array), offsetof(Test__RootMessage, f_struct_array), &test__struct__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_map_int32", 15, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, n_f_map_int32), offsetof(Test__RootMessage, f_map_int32), &test__root_message__fmap_int32_entry__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_map_bool", 16, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, n_f_map_bool), offsetof(Test__RootMessage, f_map_bool), &test__root_message__fmap_bool_entry__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_map_string", 17, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, n_f_map_string), offsetof(Test__RootMessage, f_map_string), &test__root_message__fmap_string_entry__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_map_float", 18, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, n_f_map_float), offsetof(Test__RootMessage, f_map_float), &test__root_message__fmap_float_entry__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_map_struct", 19, PROTOBUF_C_LABEL_REPEATED, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, n_f_map_struct), offsetof(Test__RootMessage, f_map_struct), &test__root_message__fmap_struct_entry__descriptor, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_oneof_string", 20, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, offsetof(Test__RootMessage, f_oneof_case), offsetof(Test__RootMessage, f_oneof_string), NULL, NULL, 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_oneof_struct", 21, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_MESSAGE, offsetof(Test__RootMessage, f_oneof_case), offsetof(Test__RootMessage, f_oneof_struct), &test__map_struct__descriptor, NULL, 0 | PROTOBUF_C_FIELD_FLAG_ONEOF, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_ignored", 22, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage, f_ignored), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, { "f_renamed", 23, PROTOBUF_C_LABEL_OPTIONAL, PROTOBUF_C_TYPE_STRING, 0, /* quantifier_offset */ offsetof(Test__RootMessage, f_renamed), NULL, NULL, 0, /* flags */ 0, NULL, NULL /* reserved1,reserved2, etc */ }, }; static const unsigned test__root_message__field_indices_by_name[] = { 6, /* field[6] = f_bool */ 8, /* field[8] = f_bytes */ 17, /* field[17] = f_ignored */ 0, /* field[0] = f_int32 */ 1, /* field[1] = f_int64 */ 11, /* field[11] = f_map_bool */ 13, /* field[13] = f_map_float */ 10, /* field[10] = f_map_int32 */ 12, /* field[12] = f_map_string */ 14, /* field[14] = f_map_struct */ 15, /* field[15] = f_oneof_string */ 16, /* field[16] = f_oneof_struct */ 18, /* field[18] = f_renamed */ 4, /* field[4] = f_sfixed32 */ 5, /* field[5] = f_sfixed64 */ 2, /* field[2] = f_sint32 */ 3, /* field[3] = f_sint64 */ 7, /* field[7] = f_string */ 9, /* field[9] = f_struct_array */ }; static const ProtobufCIntRange test__root_message__number_ranges[3 + 1] = { {1, 0}, {5, 2}, {9, 4}, {0, 19}}; const ProtobufCMessageDescriptor test__root_message__descriptor = { PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, "test.RootMessage", "RootMessage", "Test__RootMessage", "test", sizeof(Test__RootMessage), 19, test__root_message__field_descriptors, test__root_message__field_indices_by_name, 3, test__root_message__number_ranges, (ProtobufCMessageInit) test__root_message__init, NULL, NULL, NULL /* reserved[123] */ }; yara-4.1.3/libyara/modules/pb_tests/pb_tests.pb-c.h000066400000000000000000000270361413423160300222120ustar00rootroot00000000000000/* Generated by the protocol buffer compiler. DO NOT EDIT! */ /* Generated from: modules/pb_tests/pb_tests.proto */ #ifndef PROTOBUF_C_modules_2fpb_5ftests_2fpb_5ftests_2eproto__INCLUDED #define PROTOBUF_C_modules_2fpb_5ftests_2fpb_5ftests_2eproto__INCLUDED #include PROTOBUF_C__BEGIN_DECLS #if PROTOBUF_C_VERSION_NUMBER < 1000000 # error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. #elif 1003002 < PROTOBUF_C_MIN_COMPILER_VERSION # error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. #endif #include "yara.pb-c.h" typedef struct _Test__Struct Test__Struct; typedef struct _Test__Struct__NestedStruct Test__Struct__NestedStruct; typedef struct _Test__MapStruct Test__MapStruct; typedef struct _Test__RootMessage Test__RootMessage; typedef struct _Test__RootMessage__FMapInt32Entry Test__RootMessage__FMapInt32Entry; typedef struct _Test__RootMessage__FMapBoolEntry Test__RootMessage__FMapBoolEntry; typedef struct _Test__RootMessage__FMapStringEntry Test__RootMessage__FMapStringEntry; typedef struct _Test__RootMessage__FMapFloatEntry Test__RootMessage__FMapFloatEntry; typedef struct _Test__RootMessage__FMapStructEntry Test__RootMessage__FMapStructEntry; /* --- enums --- */ typedef enum _Test__Struct__Enum { TEST__STRUCT__ENUM__FIRST = 0, TEST__STRUCT__ENUM__SECOND = 1 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(TEST__STRUCT__ENUM) } Test__Struct__Enum; /* --- messages --- */ struct _Test__Struct__NestedStruct { ProtobufCMessage base; protobuf_c_boolean has_f_int32; int32_t f_int32; char *f_string; }; #define TEST__STRUCT__NESTED_STRUCT__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__struct__nested_struct__descriptor) \ , 0, 0, NULL } struct _Test__Struct { ProtobufCMessage base; char *f_string; protobuf_c_boolean has_f_enum; Test__Struct__Enum f_enum; Test__Struct__NestedStruct *f_nested_struct; size_t n_f_nested_struct_array; Test__Struct__NestedStruct **f_nested_struct_array; }; #define TEST__STRUCT__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__struct__descriptor) \ , NULL, 0, TEST__STRUCT__ENUM__FIRST, NULL, 0,NULL } struct _Test__MapStruct { ProtobufCMessage base; protobuf_c_boolean has_f_int32; int32_t f_int32; protobuf_c_boolean has_f_int64; int64_t f_int64; }; #define TEST__MAP_STRUCT__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__map_struct__descriptor) \ , 0, 0, 0, 0 } struct _Test__RootMessage__FMapInt32Entry { ProtobufCMessage base; char *key; protobuf_c_boolean has_value; int32_t value; }; #define TEST__ROOT_MESSAGE__FMAP_INT32_ENTRY__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__root_message__fmap_int32_entry__descriptor) \ , NULL, 0, 0 } struct _Test__RootMessage__FMapBoolEntry { ProtobufCMessage base; char *key; protobuf_c_boolean has_value; protobuf_c_boolean value; }; #define TEST__ROOT_MESSAGE__FMAP_BOOL_ENTRY__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__root_message__fmap_bool_entry__descriptor) \ , NULL, 0, 0 } struct _Test__RootMessage__FMapStringEntry { ProtobufCMessage base; char *key; char *value; }; #define TEST__ROOT_MESSAGE__FMAP_STRING_ENTRY__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__root_message__fmap_string_entry__descriptor) \ , NULL, NULL } struct _Test__RootMessage__FMapFloatEntry { ProtobufCMessage base; char *key; protobuf_c_boolean has_value; float value; }; #define TEST__ROOT_MESSAGE__FMAP_FLOAT_ENTRY__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__root_message__fmap_float_entry__descriptor) \ , NULL, 0, 0 } struct _Test__RootMessage__FMapStructEntry { ProtobufCMessage base; char *key; Test__MapStruct *value; }; #define TEST__ROOT_MESSAGE__FMAP_STRUCT_ENTRY__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__root_message__fmap_struct_entry__descriptor) \ , NULL, NULL } typedef enum { TEST__ROOT_MESSAGE__F_ONEOF__NOT_SET = 0, TEST__ROOT_MESSAGE__F_ONEOF_F_ONEOF_STRING = 20, TEST__ROOT_MESSAGE__F_ONEOF_F_ONEOF_STRUCT = 21 PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(TEST__ROOT_MESSAGE__F_ONEOF) } Test__RootMessage__FOneofCase; struct _Test__RootMessage { ProtobufCMessage base; protobuf_c_boolean has_f_int32; int32_t f_int32; protobuf_c_boolean has_f_int64; int64_t f_int64; /* *optional uint32 f_uint32 = 3; // not supported *optional uint64 f_uint64 = 4; // not supported */ protobuf_c_boolean has_f_sint32; int32_t f_sint32; protobuf_c_boolean has_f_sint64; int64_t f_sint64; /* *optional fixed32 f_fixed32 = 7; // not supported *optional fixed64 f_fixed64 = 8; // not supported */ protobuf_c_boolean has_f_sfixed32; int32_t f_sfixed32; protobuf_c_boolean has_f_sfixed64; int64_t f_sfixed64; protobuf_c_boolean has_f_bool; protobuf_c_boolean f_bool; char *f_string; protobuf_c_boolean has_f_bytes; ProtobufCBinaryData f_bytes; size_t n_f_struct_array; Test__Struct **f_struct_array; size_t n_f_map_int32; Test__RootMessage__FMapInt32Entry **f_map_int32; size_t n_f_map_bool; Test__RootMessage__FMapBoolEntry **f_map_bool; size_t n_f_map_string; Test__RootMessage__FMapStringEntry **f_map_string; size_t n_f_map_float; Test__RootMessage__FMapFloatEntry **f_map_float; size_t n_f_map_struct; Test__RootMessage__FMapStructEntry **f_map_struct; char *f_ignored; char *f_renamed; Test__RootMessage__FOneofCase f_oneof_case; union { char *f_oneof_string; Test__MapStruct *f_oneof_struct; }; }; #define TEST__ROOT_MESSAGE__INIT \ { PROTOBUF_C_MESSAGE_INIT (&test__root_message__descriptor) \ , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0, {0,NULL}, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, 0,NULL, NULL, NULL, TEST__ROOT_MESSAGE__F_ONEOF__NOT_SET, {0} } /* Test__Struct__NestedStruct methods */ void test__struct__nested_struct__init (Test__Struct__NestedStruct *message); /* Test__Struct methods */ void test__struct__init (Test__Struct *message); size_t test__struct__get_packed_size (const Test__Struct *message); size_t test__struct__pack (const Test__Struct *message, uint8_t *out); size_t test__struct__pack_to_buffer (const Test__Struct *message, ProtobufCBuffer *buffer); Test__Struct * test__struct__unpack (ProtobufCAllocator *allocator, size_t len, const uint8_t *data); void test__struct__free_unpacked (Test__Struct *message, ProtobufCAllocator *allocator); /* Test__MapStruct methods */ void test__map_struct__init (Test__MapStruct *message); size_t test__map_struct__get_packed_size (const Test__MapStruct *message); size_t test__map_struct__pack (const Test__MapStruct *message, uint8_t *out); size_t test__map_struct__pack_to_buffer (const Test__MapStruct *message, ProtobufCBuffer *buffer); Test__MapStruct * test__map_struct__unpack (ProtobufCAllocator *allocator, size_t len, const uint8_t *data); void test__map_struct__free_unpacked (Test__MapStruct *message, ProtobufCAllocator *allocator); /* Test__RootMessage__FMapInt32Entry methods */ void test__root_message__fmap_int32_entry__init (Test__RootMessage__FMapInt32Entry *message); /* Test__RootMessage__FMapBoolEntry methods */ void test__root_message__fmap_bool_entry__init (Test__RootMessage__FMapBoolEntry *message); /* Test__RootMessage__FMapStringEntry methods */ void test__root_message__fmap_string_entry__init (Test__RootMessage__FMapStringEntry *message); /* Test__RootMessage__FMapFloatEntry methods */ void test__root_message__fmap_float_entry__init (Test__RootMessage__FMapFloatEntry *message); /* Test__RootMessage__FMapStructEntry methods */ void test__root_message__fmap_struct_entry__init (Test__RootMessage__FMapStructEntry *message); /* Test__RootMessage methods */ void test__root_message__init (Test__RootMessage *message); size_t test__root_message__get_packed_size (const Test__RootMessage *message); size_t test__root_message__pack (const Test__RootMessage *message, uint8_t *out); size_t test__root_message__pack_to_buffer (const Test__RootMessage *message, ProtobufCBuffer *buffer); Test__RootMessage * test__root_message__unpack (ProtobufCAllocator *allocator, size_t len, const uint8_t *data); void test__root_message__free_unpacked (Test__RootMessage *message, ProtobufCAllocator *allocator); /* --- per-message closures --- */ typedef void (*Test__Struct__NestedStruct_Closure) (const Test__Struct__NestedStruct *message, void *closure_data); typedef void (*Test__Struct_Closure) (const Test__Struct *message, void *closure_data); typedef void (*Test__MapStruct_Closure) (const Test__MapStruct *message, void *closure_data); typedef void (*Test__RootMessage__FMapInt32Entry_Closure) (const Test__RootMessage__FMapInt32Entry *message, void *closure_data); typedef void (*Test__RootMessage__FMapBoolEntry_Closure) (const Test__RootMessage__FMapBoolEntry *message, void *closure_data); typedef void (*Test__RootMessage__FMapStringEntry_Closure) (const Test__RootMessage__FMapStringEntry *message, void *closure_data); typedef void (*Test__RootMessage__FMapFloatEntry_Closure) (const Test__RootMessage__FMapFloatEntry *message, void *closure_data); typedef void (*Test__RootMessage__FMapStructEntry_Closure) (const Test__RootMessage__FMapStructEntry *message, void *closure_data); typedef void (*Test__RootMessage_Closure) (const Test__RootMessage *message, void *closure_data); /* --- services --- */ /* --- descriptors --- */ extern const ProtobufCMessageDescriptor test__struct__descriptor; extern const ProtobufCMessageDescriptor test__struct__nested_struct__descriptor; extern const ProtobufCEnumDescriptor test__struct__enum__descriptor; extern const ProtobufCMessageDescriptor test__map_struct__descriptor; extern const ProtobufCMessageDescriptor test__root_message__descriptor; extern const ProtobufCMessageDescriptor test__root_message__fmap_int32_entry__descriptor; extern const ProtobufCMessageDescriptor test__root_message__fmap_bool_entry__descriptor; extern const ProtobufCMessageDescriptor test__root_message__fmap_string_entry__descriptor; extern const ProtobufCMessageDescriptor test__root_message__fmap_float_entry__descriptor; extern const ProtobufCMessageDescriptor test__root_message__fmap_struct_entry__descriptor; PROTOBUF_C__END_DECLS #endif /* PROTOBUF_C_modules_2fpb_5ftests_2fpb_5ftests_2eproto__INCLUDED */ yara-4.1.3/libyara/modules/pb_tests/pb_tests.proto000066400000000000000000000031571413423160300223040ustar00rootroot00000000000000 syntax = "proto2"; package test; import "yara.proto"; option (yara.module_options) = { name : "pb_tests" root_message: "RootMessage"; }; message Struct { option (yara.message_options).name = "struct"; required string f_string = 1; enum Enum { option (yara.enum_options).name = "enum"; FIRST = 0; SECOND = 1; } optional Enum f_enum = 2; message NestedStruct { optional int32 f_int32 = 1; optional string f_string = 2; } optional NestedStruct f_nested_struct = 3; repeated NestedStruct f_nested_struct_array = 4; } message MapStruct { optional int32 f_int32 = 1; optional int64 f_int64 = 2; } message RootMessage { optional int32 f_int32 = 1; optional int64 f_int64 = 2; //optional uint32 f_uint32 = 3; // not supported //optional uint64 f_uint64 = 4; // not supported optional sint32 f_sint32 = 5; optional sint64 f_sint64 = 6; //optional fixed32 f_fixed32 = 7; // not supported //optional fixed64 f_fixed64 = 8; // not supported optional sfixed32 f_sfixed32 = 9; optional sfixed64 f_sfixed64 = 10; optional bool f_bool = 11; optional string f_string = 12; optional bytes f_bytes = 13; repeated Struct f_struct_array = 14; map f_map_int32 = 15; map f_map_bool = 16; map f_map_string = 17; map f_map_float = 18; map f_map_struct = 19; oneof f_oneof { string f_oneof_string = 20; MapStruct f_oneof_struct = 21; } optional string f_ignored = 22 [(yara.field_options).ignore = true]; optional string f_renamed = 23 [(yara.field_options).name = "f_yara_name"]; } yara-4.1.3/libyara/modules/pb_tests/yara.pb-c.h000066400000000000000000000002021413423160300213050ustar00rootroot00000000000000 /* * Empty header file generated by protoc-gen-yara because it is included from * .pb-c.h files generated by protoc-gen-c. */ yara-4.1.3/libyara/modules/pb_to_module.rst000066400000000000000000000017401413423160300207470ustar00rootroot00000000000000Generating a module from a Protocol Buffer [Protocol Buffers](https://developers.google.com/protocol-buffers) (protobufs) are Google's language-neutral, platform-idependent mechanism for serializing structured data. The first thing you need to do for using protobuf is defining your data structures, for example: message Employee { int32 id = 1; string name = 2; int32 age = 3 string email = 4; } Once you have defined your data structure, you use a protobuf compiler to automatically generate the code that will marshal/unmarshall the data structure into/from a bytes sequence. The protobuf compiler is able to generate code in multiple languages, including C/C++, Python, Java and Go. Now imagine that you can pass the marshalled data structure to YARA, and create rules based in that data. Like for example: import "vt_employee" rule virustotal_employee_under_25 { condition: vt_employee.age < 25 and vt_employee.email matches /*.@virustotal\.com/ } Neat, right? yara-4.1.3/libyara/modules/pe/000077500000000000000000000000001413423160300161475ustar00rootroot00000000000000yara-4.1.3/libyara/modules/pe/pe.c000066400000000000000000002630731413423160300167320ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "../crypto.h" #if defined(HAVE_LIBCRYPTO) #include #include #include #include #include #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) #define X509_get_signature_nid(o) OBJ_obj2nid((o)->sig_alg->algorithm) #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L #define X509_get0_notBefore X509_get_notBefore #define X509_get0_notAfter X509_get_notAfter #endif #endif #include #include #include #include #include #include #include #define MODULE_NAME pe // http://msdn.microsoft.com/en-us/library/ms648009(v=vs.85).aspx #define RESOURCE_TYPE_CURSOR 1 #define RESOURCE_TYPE_BITMAP 2 #define RESOURCE_TYPE_ICON 3 #define RESOURCE_TYPE_MENU 4 #define RESOURCE_TYPE_DIALOG 5 #define RESOURCE_TYPE_STRING 6 #define RESOURCE_TYPE_FONTDIR 7 #define RESOURCE_TYPE_FONT 8 #define RESOURCE_TYPE_ACCELERATOR 9 #define RESOURCE_TYPE_RCDATA 10 #define RESOURCE_TYPE_MESSAGETABLE 11 #define RESOURCE_TYPE_GROUP_CURSOR \ 12 // MAKEINTRESOURCE((ULONG_PTR)(RT_CURSOR) + 11) #define RESOURCE_TYPE_GROUP_ICON \ 14 // MAKEINTRESOURCE((ULONG_PTR)(RT_ICON) + 11) #define RESOURCE_TYPE_VERSION 16 #define RESOURCE_TYPE_DLGINCLUDE 17 #define RESOURCE_TYPE_PLUGPLAY 19 #define RESOURCE_TYPE_VXD 20 #define RESOURCE_TYPE_ANICURSOR 21 #define RESOURCE_TYPE_ANIICON 22 #define RESOURCE_TYPE_HTML 23 #define RESOURCE_TYPE_MANIFEST 24 #define RESOURCE_CALLBACK_CONTINUE 0 #define RESOURCE_CALLBACK_ABORT 1 #define RESOURCE_ITERATOR_FINISHED 0 #define RESOURCE_ITERATOR_ABORTED 1 #define MAX_PE_IMPORTS 16384 #define MAX_PE_EXPORTS 8192 #define MAX_EXPORT_NAME_LENGTH 512 #define MAX_RESOURCES 65536 #define IS_RESOURCE_SUBDIRECTORY(entry) \ (yr_le32toh((entry)->OffsetToData) & 0x80000000) #define RESOURCE_OFFSET(entry) (yr_le32toh((entry)->OffsetToData) & 0x7FFFFFFF) typedef int (*RESOURCE_CALLBACK_FUNC)( PIMAGE_RESOURCE_DATA_ENTRY rsrc_data, int rsrc_type, int rsrc_id, int rsrc_language, const uint8_t* type_string, const uint8_t* name_string, const uint8_t* lang_string, void* cb_data); static size_t available_space(PE* pe, void* pointer) { if ((uint8_t*) pointer < pe->data) return 0; if ((uint8_t*) pointer >= pe->data + pe->data_size) return 0; return pe->data + pe->data_size - (uint8_t*) pointer; } static int wide_string_fits_in_pe(PE* pe, char* data) { size_t i = 0; size_t space_left = available_space(pe, data); while (space_left >= 2) { if (data[i] == 0 && data[i + 1] == 0) return 1; space_left -= 2; i += 2; } return 0; } // Parse the rich signature. // http://www.ntcore.com/files/richsign.htm static void pe_parse_rich_signature(PE* pe, uint64_t base_address) { PIMAGE_DOS_HEADER mz_header; PRICH_SIGNATURE rich_signature = NULL; DWORD* rich_ptr = NULL; BYTE* raw_data = NULL; BYTE* clear_data = NULL; DWORD* p = NULL; uint32_t nthdr_offset = 0; uint32_t key = 0; size_t rich_len = 0; if (pe->data_size < sizeof(IMAGE_DOS_HEADER)) return; mz_header = (PIMAGE_DOS_HEADER) pe->data; if (yr_le16toh(mz_header->e_magic) != IMAGE_DOS_SIGNATURE) return; // To find the Rich marker we start at the NT header and work backwards, so // make sure we have at least enough data to get to the NT header. nthdr_offset = yr_le32toh(mz_header->e_lfanew); if (nthdr_offset > pe->data_size + sizeof(uint32_t) || nthdr_offset < 4) return; // Most files have the Rich header at offset 0x80, but that is not always // true. 582ce3eea9c97d5e89f7d83953a6d518b16770e635a19a456c0225449c6967a4 is // one sample which has a Rich header starting at offset 0x200. To properly // find the Rich header we need to start at the NT header and work backwards. p = (DWORD*) (pe->data + nthdr_offset - 4); while (p > (DWORD*) (pe->data + sizeof(IMAGE_DOS_HEADER))) { if (yr_le32toh(*p) == RICH_RICH) { // The XOR key is the dword following the Rich value. We use this to find // DanS header only. key = *(p + 1); rich_ptr = p; --p; break; } // The NT header is 8 byte aligned so we can move back in 4 byte increments. --p; } // If we haven't found a key we can skip processing the rest. if (key == 0) return; // If we have found the key we need to now find the start (DanS). while (p > (DWORD*) (pe->data + sizeof(IMAGE_DOS_HEADER))) { if (yr_le32toh((*(p) ^ key)) == RICH_DANS) { rich_signature = (PRICH_SIGNATURE) p; break; } --p; } if (rich_signature == NULL) return; // The three key values must all be equal and the first dword // XORs to "DanS". Then walk the buffer looking for "Rich" which marks the // end. Technically the XOR key should be right after "Rich" but it's not // important. if (yr_le32toh(rich_signature->key1) != yr_le32toh(rich_signature->key2) || yr_le32toh(rich_signature->key2) != yr_le32toh(rich_signature->key3) || (yr_le32toh(rich_signature->dans) ^ yr_le32toh(rich_signature->key1)) != RICH_DANS) { return; } // Multiple by 4 because we are counting in DWORDs. rich_len = (rich_ptr - (DWORD*) rich_signature) * 4; raw_data = (BYTE*) yr_malloc(rich_len); if (!raw_data) return; memcpy(raw_data, rich_signature, rich_len); set_integer( base_address + ((uint8_t*) rich_signature - pe->data), pe->object, "rich_signature.offset"); set_integer(rich_len, pe->object, "rich_signature.length"); set_integer( yr_le32toh(rich_signature->key1), pe->object, "rich_signature.key"); clear_data = (BYTE*) yr_malloc(rich_len); if (!clear_data) { yr_free(raw_data); return; } // Copy the entire block here to be XORed. memcpy(clear_data, raw_data, rich_len); for (rich_ptr = (DWORD*) clear_data; rich_ptr < (DWORD*) (clear_data + rich_len); rich_ptr++) { *rich_ptr ^= rich_signature->key1; } set_sized_string( (char*) raw_data, rich_len, pe->object, "rich_signature.raw_data"); set_sized_string( (char*) clear_data, rich_len, pe->object, "rich_signature.clear_data"); yr_free(raw_data); yr_free(clear_data); } static void pe_parse_debug_directory(PE* pe) { PIMAGE_DATA_DIRECTORY data_dir; PIMAGE_DEBUG_DIRECTORY debug_dir; int64_t debug_dir_offset; int64_t pcv_hdr_offset; int i, dcount; size_t pdb_path_len; char* pdb_path = NULL; data_dir = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_DEBUG); if (data_dir == NULL) return; if (yr_le32toh(data_dir->Size) == 0) return; if (yr_le32toh(data_dir->Size) % sizeof(IMAGE_DEBUG_DIRECTORY) != 0) return; if (yr_le32toh(data_dir->VirtualAddress) == 0) return; debug_dir_offset = pe_rva_to_offset(pe, yr_le32toh(data_dir->VirtualAddress)); if (debug_dir_offset < 0) return; dcount = yr_le32toh(data_dir->Size) / sizeof(IMAGE_DEBUG_DIRECTORY); for (i = 0; i < dcount; i++) { debug_dir = (PIMAGE_DEBUG_DIRECTORY)( pe->data + debug_dir_offset + i * sizeof(IMAGE_DEBUG_DIRECTORY)); if (!struct_fits_in_pe(pe, debug_dir, IMAGE_DEBUG_DIRECTORY)) break; if (yr_le32toh(debug_dir->Type) != IMAGE_DEBUG_TYPE_CODEVIEW) continue; if (yr_le32toh(debug_dir->AddressOfRawData) == 0) continue; pcv_hdr_offset = pe_rva_to_offset( pe, yr_le32toh(debug_dir->AddressOfRawData)); if (pcv_hdr_offset < 0) continue; PCV_HEADER cv_hdr = (PCV_HEADER)(pe->data + pcv_hdr_offset); if (!struct_fits_in_pe(pe, cv_hdr, CV_HEADER)) continue; if (yr_le32toh(cv_hdr->dwSignature) == CVINFO_PDB20_CVSIGNATURE) { PCV_INFO_PDB20 pdb20 = (PCV_INFO_PDB20) cv_hdr; if (struct_fits_in_pe(pe, pdb20, CV_INFO_PDB20)) pdb_path = (char*) (pdb20->PdbFileName); } else if (yr_le32toh(cv_hdr->dwSignature) == CVINFO_PDB70_CVSIGNATURE) { PCV_INFO_PDB70 pdb70 = (PCV_INFO_PDB70) cv_hdr; if (struct_fits_in_pe(pe, pdb70, CV_INFO_PDB70)) pdb_path = (char*) (pdb70->PdbFileName); } if (pdb_path != NULL) { pdb_path_len = strnlen( pdb_path, yr_min(available_space(pe, pdb_path), MAX_PATH)); if (pdb_path_len > 0 && pdb_path_len < MAX_PATH) { set_sized_string(pdb_path, pdb_path_len, pe->object, "pdb_path"); break; } } } } // Return a pointer to the resource directory string or NULL. // The callback function will parse this and call set_sized_string(). // The pointer is guaranteed to have enough space to contain the entire string. static const uint8_t* parse_resource_name( PE* pe, const uint8_t* rsrc_data, PIMAGE_RESOURCE_DIRECTORY_ENTRY entry) { // If high bit is set it is an offset relative to rsrc_data, which contains // a resource directory string. if (yr_le32toh(entry->Name) & 0x80000000) { DWORD length; const uint8_t* rsrc_str_ptr = rsrc_data + (yr_le32toh(entry->Name) & 0x7FFFFFFF); // A resource directory string is 2 bytes for the length and then a variable // length Unicode string. Make sure we have at least 2 bytes. if (!fits_in_pe(pe, rsrc_str_ptr, 2)) return NULL; length = *rsrc_str_ptr; // Move past the length and make sure we have enough bytes for the string. if (!fits_in_pe(pe, rsrc_str_ptr + 2, length * 2)) return NULL; return rsrc_str_ptr; } return NULL; } static int _pe_iterate_resources( PE* pe, PIMAGE_RESOURCE_DIRECTORY resource_dir, const uint8_t* rsrc_data, int rsrc_tree_level, int* type, int* id, int* language, const uint8_t* type_string, const uint8_t* name_string, const uint8_t* lang_string, RESOURCE_CALLBACK_FUNC callback, void* callback_data) { int i, result = RESOURCE_ITERATOR_FINISHED; int total_entries; PIMAGE_RESOURCE_DIRECTORY_ENTRY entry; // A few sanity checks to avoid corrupt files if (yr_le32toh(resource_dir->Characteristics) != 0 || yr_le16toh(resource_dir->NumberOfNamedEntries) > 32768 || yr_le16toh(resource_dir->NumberOfIdEntries) > 32768) { return result; } total_entries = yr_le16toh(resource_dir->NumberOfNamedEntries) + yr_le16toh(resource_dir->NumberOfIdEntries); // The first directory entry is just after the resource directory, // by incrementing resource_dir we skip sizeof(resource_dir) bytes // and get a pointer to the end of the resource directory. entry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(resource_dir + 1); for (i = 0; i < total_entries; i++) { if (!struct_fits_in_pe(pe, entry, IMAGE_RESOURCE_DIRECTORY_ENTRY)) { result = RESOURCE_ITERATOR_ABORTED; break; } switch (rsrc_tree_level) { case 0: *type = yr_le32toh(entry->Name); type_string = parse_resource_name(pe, rsrc_data, entry); break; case 1: *id = yr_le32toh(entry->Name); name_string = parse_resource_name(pe, rsrc_data, entry); break; case 2: *language = yr_le32toh(entry->Name); lang_string = parse_resource_name(pe, rsrc_data, entry); break; } if (IS_RESOURCE_SUBDIRECTORY(entry) && rsrc_tree_level < 2) { PIMAGE_RESOURCE_DIRECTORY directory = (PIMAGE_RESOURCE_DIRECTORY)( rsrc_data + RESOURCE_OFFSET(entry)); if (struct_fits_in_pe(pe, directory, IMAGE_RESOURCE_DIRECTORY)) { result = _pe_iterate_resources( pe, directory, rsrc_data, rsrc_tree_level + 1, type, id, language, type_string, name_string, lang_string, callback, callback_data); } else { result = RESOURCE_ITERATOR_ABORTED; } } else { PIMAGE_RESOURCE_DATA_ENTRY data_entry = (PIMAGE_RESOURCE_DATA_ENTRY)( rsrc_data + RESOURCE_OFFSET(entry)); if (struct_fits_in_pe(pe, data_entry, IMAGE_RESOURCE_DATA_ENTRY)) { if (callback( data_entry, *type, *id, *language, type_string, name_string, lang_string, callback_data) == RESOURCE_CALLBACK_ABORT) { result = RESOURCE_ITERATOR_ABORTED; } } else { result = RESOURCE_ITERATOR_ABORTED; } } if (result == RESOURCE_ITERATOR_ABORTED) break; entry++; } return result; } static int pe_iterate_resources( PE* pe, RESOURCE_CALLBACK_FUNC callback, void* callback_data) { int64_t offset; int type = -1; int id = -1; int language = -1; uint8_t* type_string = NULL; uint8_t* name_string = NULL; uint8_t* lang_string = NULL; PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry( pe, IMAGE_DIRECTORY_ENTRY_RESOURCE); if (directory == NULL) return 0; if (yr_le32toh(directory->VirtualAddress) != 0) { PIMAGE_RESOURCE_DIRECTORY rsrc_dir; offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress)); if (offset < 0) return 0; rsrc_dir = (PIMAGE_RESOURCE_DIRECTORY)(pe->data + offset); if (struct_fits_in_pe(pe, rsrc_dir, IMAGE_RESOURCE_DIRECTORY)) { set_integer( yr_le32toh(rsrc_dir->TimeDateStamp), pe->object, "resource_timestamp"); set_integer( yr_le16toh(rsrc_dir->MajorVersion), pe->object, "resource_version.major"); set_integer( yr_le16toh(rsrc_dir->MinorVersion), pe->object, "resource_version.minor"); _pe_iterate_resources( pe, rsrc_dir, pe->data + offset, 0, &type, &id, &language, type_string, name_string, lang_string, callback, callback_data); return 1; } } return 0; } // Align offset to a 32-bit boundary and add it to a pointer #define ADD_OFFSET(ptr, offset) \ (PVERSION_INFO)((uint8_t*) (ptr) + ((offset + 3) & ~3)) static void pe_parse_version_info(PIMAGE_RESOURCE_DATA_ENTRY rsrc_data, PE* pe) { PVERSION_INFO version_info; int64_t version_info_offset = pe_rva_to_offset( pe, yr_le32toh(rsrc_data->OffsetToData)); if (version_info_offset < 0) return; version_info = (PVERSION_INFO)(pe->data + version_info_offset); if (!struct_fits_in_pe(pe, version_info, VERSION_INFO)) return; if (!fits_in_pe(pe, version_info->Key, sizeof("VS_VERSION_INFO") * 2)) return; if (strcmp_w(version_info->Key, "VS_VERSION_INFO") != 0) return; version_info = ADD_OFFSET(version_info, sizeof(VERSION_INFO) + 86); while (fits_in_pe(pe, version_info->Key, sizeof("VarFileInfo") * 2) && strcmp_w(version_info->Key, "VarFileInfo") == 0 && yr_le16toh(version_info->Length) != 0) { version_info = ADD_OFFSET(version_info, yr_le16toh(version_info->Length)); } while (fits_in_pe(pe, version_info->Key, sizeof("StringFileInfo") * 2) && strcmp_w(version_info->Key, "StringFileInfo") == 0 && yr_le16toh(version_info->Length) != 0) { PVERSION_INFO string_table = ADD_OFFSET( version_info, sizeof(VERSION_INFO) + 30); version_info = ADD_OFFSET(version_info, yr_le16toh(version_info->Length)); while (struct_fits_in_pe(pe, string_table, VERSION_INFO) && wide_string_fits_in_pe(pe, string_table->Key) && yr_le16toh(string_table->Length) != 0 && string_table < version_info) { PVERSION_INFO string = ADD_OFFSET( string_table, sizeof(VERSION_INFO) + 2 * (strnlen_w(string_table->Key) + 1)); string_table = ADD_OFFSET(string_table, yr_le16toh(string_table->Length)); while (struct_fits_in_pe(pe, string, VERSION_INFO) && wide_string_fits_in_pe(pe, string->Key) && yr_le16toh(string->Length) != 0 && string < string_table) { if (yr_le16toh(string->ValueLength) > 0) { char* string_value = (char*) ADD_OFFSET( string, sizeof(VERSION_INFO) + 2 * (strnlen_w(string->Key) + 1)); if (wide_string_fits_in_pe(pe, string_value)) { char key[64]; char value[256]; strlcpy_w(key, string->Key, sizeof(key)); strlcpy_w(value, string_value, sizeof(value)); set_string(value, pe->object, "version_info[%s]", key); } } string = ADD_OFFSET(string, yr_le16toh(string->Length)); } } } } static int pe_collect_resources( PIMAGE_RESOURCE_DATA_ENTRY rsrc_data, int rsrc_type, int rsrc_id, int rsrc_language, uint8_t* type_string, uint8_t* name_string, uint8_t* lang_string, PE* pe) { DWORD length; // Don't collect too many resources. if (pe->resources > MAX_RESOURCES) return RESOURCE_CALLBACK_CONTINUE; set_integer( yr_le32toh(rsrc_data->OffsetToData), pe->object, "resources[%i].rva", pe->resources); int64_t offset = pe_rva_to_offset(pe, yr_le32toh(rsrc_data->OffsetToData)); if (offset < 0) offset = YR_UNDEFINED; set_integer(offset, pe->object, "resources[%i].offset", pe->resources); set_integer( yr_le32toh(rsrc_data->Size), pe->object, "resources[%i].length", pe->resources); if (type_string) { // Multiply by 2 because it is a Unicode string. length = ((DWORD) *type_string) * 2; type_string += 2; set_sized_string( (char*) type_string, length, pe->object, "resources[%i].type_string", pe->resources); } else { set_integer(rsrc_type, pe->object, "resources[%i].type", pe->resources); } if (name_string) { // Multiply by 2 because it is a Unicode string. length = ((DWORD) *name_string) * 2; name_string += 2; set_sized_string( (char*) name_string, length, pe->object, "resources[%i].name_string", pe->resources); } else { set_integer(rsrc_id, pe->object, "resources[%i].id", pe->resources); } if (lang_string) { // Multiply by 2 because it is a Unicode string. length = ((DWORD) *lang_string) * 2; lang_string += 2; set_sized_string( (char*) lang_string, length, pe->object, "resources[%i].language_string", pe->resources); } else { set_integer( rsrc_language, pe->object, "resources[%i].language", pe->resources); } // Resources we do extra parsing on if (rsrc_type == RESOURCE_TYPE_VERSION) pe_parse_version_info(rsrc_data, pe); pe->resources += 1; return RESOURCE_CALLBACK_CONTINUE; } static IMPORT_FUNCTION* pe_parse_import_descriptor( PE* pe, PIMAGE_IMPORT_DESCRIPTOR import_descriptor, char* dll_name, int* num_function_imports) { IMPORT_FUNCTION* head = NULL; IMPORT_FUNCTION* tail = NULL; int64_t offset = pe_rva_to_offset( pe, yr_le32toh(import_descriptor->OriginalFirstThunk)); // I've seen binaries where OriginalFirstThunk is zero. In this case // use FirstThunk. if (offset <= 0) offset = pe_rva_to_offset(pe, yr_le32toh(import_descriptor->FirstThunk)); if (offset < 0) return NULL; if (IS_64BITS_PE(pe)) { PIMAGE_THUNK_DATA64 thunks64 = (PIMAGE_THUNK_DATA64)(pe->data + offset); while (struct_fits_in_pe(pe, thunks64, IMAGE_THUNK_DATA64) && yr_le64toh(thunks64->u1.Ordinal) != 0 && *num_function_imports < MAX_PE_IMPORTS) { char* name = NULL; uint16_t ordinal = 0; uint8_t has_ordinal = 0; if (!(yr_le64toh(thunks64->u1.Ordinal) & IMAGE_ORDINAL_FLAG64)) { // If imported by name offset = pe_rva_to_offset(pe, yr_le64toh(thunks64->u1.Function)); if (offset >= 0) { PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)( pe->data + offset); if (struct_fits_in_pe(pe, import, IMAGE_IMPORT_BY_NAME)) { name = (char*) yr_strndup( (char*) import->Name, yr_min(available_space(pe, import->Name), 512)); } } } else { // If imported by ordinal. Lookup the ordinal. name = ord_lookup(dll_name, yr_le64toh(thunks64->u1.Ordinal) & 0xFFFF); // Also store the ordinal. ordinal = yr_le64toh(thunks64->u1.Ordinal) & 0xFFFF; has_ordinal = 1; } if (name != NULL || has_ordinal == 1) { IMPORT_FUNCTION* imported_func = (IMPORT_FUNCTION*) yr_calloc( 1, sizeof(IMPORT_FUNCTION)); if (imported_func == NULL) { yr_free(name); continue; } imported_func->name = name; imported_func->ordinal = ordinal; imported_func->has_ordinal = has_ordinal; imported_func->next = NULL; if (head == NULL) head = imported_func; if (tail != NULL) tail->next = imported_func; tail = imported_func; } (*num_function_imports)++; thunks64++; } } else { PIMAGE_THUNK_DATA32 thunks32 = (PIMAGE_THUNK_DATA32)(pe->data + offset); while (struct_fits_in_pe(pe, thunks32, IMAGE_THUNK_DATA32) && yr_le32toh(thunks32->u1.Ordinal) != 0 && *num_function_imports < MAX_PE_IMPORTS) { char* name = NULL; uint16_t ordinal = 0; uint8_t has_ordinal = 0; if (!(yr_le32toh(thunks32->u1.Ordinal) & IMAGE_ORDINAL_FLAG32)) { // If imported by name offset = pe_rva_to_offset(pe, yr_le32toh(thunks32->u1.Function)); if (offset >= 0) { PIMAGE_IMPORT_BY_NAME import = (PIMAGE_IMPORT_BY_NAME)( pe->data + offset); if (struct_fits_in_pe(pe, import, IMAGE_IMPORT_BY_NAME)) { name = (char*) yr_strndup( (char*) import->Name, yr_min(available_space(pe, import->Name), 512)); } } } else { // If imported by ordinal. Lookup the ordinal. name = ord_lookup(dll_name, yr_le32toh(thunks32->u1.Ordinal) & 0xFFFF); // Also store the ordinal. ordinal = yr_le32toh(thunks32->u1.Ordinal) & 0xFFFF; has_ordinal = 1; } if (name != NULL || has_ordinal == 1) { IMPORT_FUNCTION* imported_func = (IMPORT_FUNCTION*) yr_calloc( 1, sizeof(IMPORT_FUNCTION)); if (imported_func == NULL) { yr_free(name); continue; } imported_func->name = name; imported_func->ordinal = ordinal; imported_func->has_ordinal = has_ordinal; imported_func->next = NULL; if (head == NULL) head = imported_func; if (tail != NULL) tail->next = imported_func; tail = imported_func; } (*num_function_imports)++; thunks32++; } } return head; } static int pe_valid_dll_name(const char* dll_name, size_t n) { const char* c = dll_name; size_t l = 0; while (l < n && *c != '\0') { if ((*c >= 'a' && *c <= 'z') || (*c >= 'A' && *c <= 'Z') || (*c >= '0' && *c <= '9') || (*c == '_' || *c == '.' || *c == '-' || *c == '+')) { c++; l++; } else { return false; } } return (l > 0 && l < n); } // // Walk the imports and collect relevant information. It is used in the // "imports" function for comparison and in the "imphash" function for // calculation. // static IMPORTED_DLL* pe_parse_imports(PE* pe) { int64_t offset; int num_imports = 0; // Number of imported DLLs int num_function_imports = 0; // Total number of functions imported IMPORTED_DLL* head = NULL; IMPORTED_DLL* tail = NULL; PIMAGE_IMPORT_DESCRIPTOR imports; PIMAGE_DATA_DIRECTORY directory; // Default to 0 imports until we know there are any set_integer(0, pe->object, "number_of_imports"); set_integer(0, pe->object, "number_of_imported_functions"); directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_IMPORT); if (directory == NULL) return NULL; if (yr_le32toh(directory->VirtualAddress) == 0) return NULL; offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress)); if (offset < 0) return NULL; imports = (PIMAGE_IMPORT_DESCRIPTOR)(pe->data + offset); while (struct_fits_in_pe(pe, imports, IMAGE_IMPORT_DESCRIPTOR) && yr_le32toh(imports->Name) != 0 && num_imports < MAX_PE_IMPORTS) { int64_t offset = pe_rva_to_offset(pe, yr_le32toh(imports->Name)); if (offset >= 0) { IMPORTED_DLL* imported_dll; char* dll_name = (char*) (pe->data + offset); if (!pe_valid_dll_name(dll_name, pe->data_size - (size_t) offset)) { imports++; continue; } imported_dll = (IMPORTED_DLL*) yr_calloc(1, sizeof(IMPORTED_DLL)); if (imported_dll != NULL) { IMPORT_FUNCTION* functions = pe_parse_import_descriptor( pe, imports, dll_name, &num_function_imports); if (functions != NULL) { imported_dll->name = yr_strdup(dll_name); ; imported_dll->functions = functions; imported_dll->next = NULL; if (head == NULL) head = imported_dll; if (tail != NULL) tail->next = imported_dll; tail = imported_dll; } else { yr_free(imported_dll); } } } num_imports++; imports++; } set_integer(num_imports, pe->object, "number_of_imports"); set_integer(num_function_imports, pe->object, "number_of_imported_functions"); return head; } // // Walk the exports and collect relevant information. It is used in the // "exports" function for comparison. // static void pe_parse_exports(PE* pe) { PIMAGE_DATA_DIRECTORY directory; PIMAGE_EXPORT_DIRECTORY exports; int64_t offset; int64_t export_start; uint32_t i, j; uint32_t number_of_exports; uint32_t number_of_names; uint32_t ordinal_base; size_t export_size; size_t remaining; size_t name_len; uint32_t exp_sz = 0; DWORD* names = NULL; WORD* ordinals = NULL; DWORD* function_addrs = NULL; // If not a PE file, return YR_UNDEFINED if (pe == NULL) return; // Default to 0 exports until we know there are any set_integer(0, pe->object, "number_of_exports"); directory = pe_get_directory_entry(pe, IMAGE_DIRECTORY_ENTRY_EXPORT); if (directory == NULL) return; if (yr_le32toh(directory->VirtualAddress) == 0) return; offset = pe_rva_to_offset(pe, yr_le32toh(directory->VirtualAddress)); if (offset < 0) return; export_start = offset; export_size = yr_le32toh(directory->Size); exports = (PIMAGE_EXPORT_DIRECTORY)(pe->data + offset); if (!struct_fits_in_pe(pe, exports, IMAGE_EXPORT_DIRECTORY)) return; number_of_exports = yr_min( yr_le32toh(exports->NumberOfFunctions), MAX_PE_EXPORTS); ordinal_base = yr_le32toh(exports->Base); set_integer( yr_le32toh(exports->TimeDateStamp), pe->object, "export_timestamp"); offset = pe_rva_to_offset(pe, yr_le32toh(exports->Name)); if (offset > 0) { remaining = pe->data_size - (size_t) offset; name_len = strnlen((char*) (pe->data + offset), remaining); set_sized_string( (char*) (pe->data + offset), name_len, pe->object, "dll_name"); } if (number_of_exports * sizeof(DWORD) > pe->data_size - offset) return; if (yr_le32toh(exports->NumberOfNames) > 0) { offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNames)); if (offset < 0) return; if (yr_le32toh(exports->NumberOfNames) * sizeof(DWORD) > pe->data_size - offset) return; names = (DWORD*) (pe->data + offset); } offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfNameOrdinals)); if (offset < 0) return; ordinals = (WORD*) (pe->data + offset); if (available_space(pe, ordinals) < sizeof(WORD) * number_of_exports) return; offset = pe_rva_to_offset(pe, yr_le32toh(exports->AddressOfFunctions)); if (offset < 0) return; function_addrs = (DWORD*) (pe->data + offset); if (available_space(pe, function_addrs) < sizeof(DWORD) * number_of_exports) return; number_of_names = yr_min( yr_le32toh(yr_le32toh(exports->NumberOfNames)), number_of_exports); // Mapping out the exports is a bit janky. We start with the export address // array. The index from that array plus the ordinal base is the ordinal for // that export. To find the name we walk the ordinal array looking for a value // that matches our index. If one exists we look up the corresponding RVA from // the names array and follow it to get the name. If one does not exist then // the export has no name. // // Ordinal base: 5 // 0 1 2 // Address array: [ 0x00000011 | 0x00000022 | 0x00000033 ] // 0 1 2 // Ordinal array: [ 0x0000 | 0x0002 | 0x0001 ] // 0 1 // Names array: [ 0x00000044 | 0x00000055 ] // // The function at RVA 0x00000011 (index 0) has ordinal 5 (base + index). The // index can be found in position 0 in the ordinal array. Using 0 to index // into the name array gives us an RVA (0x00000044) which we can follow to get // the name. // // The function at RVA 0x00000022 (index 1) has ordinal 6 (base + index). The // index can be found in position 2 in the ordinal array. 2 is out of bounds // for the names array so this function is exported without a name. // // The function at RVA 0x00000033 (index 2) has ordinal 7 (base + index). The // index can be found in position 1 in the ordinal array. Using 1 to index // into the name array gives us an RVA (0x00000055) which we can follow to get // the name. // // If the RVA from the address array is within the export directory it is a // forwarder RVA and points to a NULL terminated ASCII string. for (i = 0; i < number_of_exports; i++) { set_integer( ordinal_base + i, pe->object, "export_details[%i].ordinal", exp_sz); // Don't check for a failure here since some packers make this an invalid // value. offset = pe_rva_to_offset(pe, yr_le32toh(function_addrs[i])); if (offset > export_start && offset < export_start + export_size) { remaining = pe->data_size - (size_t) offset; name_len = strnlen((char*) (pe->data + offset), remaining); set_sized_string( (char*) (pe->data + offset), yr_min(name_len, MAX_EXPORT_NAME_LENGTH), pe->object, "export_details[%i].forward_name", exp_sz); } else { set_integer(offset, pe->object, "export_details[%i].offset", exp_sz); } if (names != NULL) { for (j = 0; j < number_of_exports; j++) { if (yr_le16toh(ordinals[j]) == i && j < number_of_names) { offset = pe_rva_to_offset(pe, yr_le32toh(names[j])); if (offset > 0) { remaining = pe->data_size - (size_t) offset; name_len = strnlen((char*) (pe->data + offset), remaining); set_sized_string( (char*) (pe->data + offset), yr_min(name_len, MAX_EXPORT_NAME_LENGTH), pe->object, "export_details[%i].name", exp_sz); } break; } } } exp_sz++; } set_integer(exp_sz, pe->object, "number_of_exports"); } // BoringSSL (https://boringssl.googlesource.com/boringssl/) doesn't support // some features used in pe_parse_certificates, if you are using BoringSSL // instead of OpenSSL you should define BORINGSSL for YARA to compile properly, // but you won't have signature-related features in the PE module. #if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL) // // Parse a PKCS7 blob, looking for certs and nested PKCS7 blobs. // void _parse_pkcs7(PE* pe, PKCS7* pkcs7, int* counter) { int i, j; time_t date_time; int sig_nid; char buffer[256]; int bytes; int idx; const EVP_MD* sha1_digest = EVP_sha1(); const unsigned char* p; unsigned char thumbprint[YR_SHA1_LEN]; char thumbprint_ascii[YR_SHA1_LEN * 2 + 1]; PKCS7_SIGNER_INFO* signer_info = NULL; PKCS7* nested_pkcs7 = NULL; ASN1_INTEGER* serial = NULL; ASN1_TYPE* nested = NULL; ASN1_STRING* value = NULL; X509* cert = NULL; STACK_OF(X509)* certs = NULL; X509_ATTRIBUTE* xa = NULL; STACK_OF(X509_ATTRIBUTE)* attrs = NULL; if (*counter >= MAX_PE_CERTS) return; certs = PKCS7_get0_signers(pkcs7, NULL, 0); if (!certs) return; for (i = 0; i < sk_X509_num(certs) && *counter < MAX_PE_CERTS; i++) { cert = sk_X509_value(certs, i); X509_digest(cert, sha1_digest, thumbprint, NULL); for (j = 0; j < YR_SHA1_LEN; j++) sprintf(thumbprint_ascii + (j * 2), "%02x", thumbprint[j]); set_string( (char*) thumbprint_ascii, pe->object, "signatures[%i].thumbprint", *counter); X509_NAME_oneline(X509_get_issuer_name(cert), buffer, sizeof(buffer)); set_string(buffer, pe->object, "signatures[%i].issuer", *counter); X509_NAME_oneline(X509_get_subject_name(cert), buffer, sizeof(buffer)); set_string(buffer, pe->object, "signatures[%i].subject", *counter); set_integer( X509_get_version(cert) + 1, // Versions are zero based, so add one. pe->object, "signatures[%i].version", *counter); sig_nid = X509_get_signature_nid(cert); set_string( OBJ_nid2ln(sig_nid), pe->object, "signatures[%i].algorithm", *counter); OBJ_obj2txt(buffer, sizeof(buffer), OBJ_nid2obj(sig_nid), 1); set_string(buffer, pe->object, "signatures[%i].algorithm_oid", *counter); serial = X509_get_serialNumber(cert); if (serial) { // ASN1_INTEGER can be negative (serial->type & V_ASN1_NEG_INTEGER), // in which case the serial number will be stored in 2's complement. // // Handle negative serial numbers, which are technically not allowed // by RFC5280, but do exist. An example binary which has a negative // serial number is: 4bfe05f182aa273e113db6ed7dae4bb8. // // Negative serial numbers are handled by calling i2d_ASN1_INTEGER() // with a NULL second parameter. This will return the size of the // buffer necessary to store the proper serial number. // // Do this even for positive serial numbers because it makes the code // cleaner and easier to read. bytes = i2d_ASN1_INTEGER(serial, NULL); // According to X.509 specification the maximum length for the // serial number is 20 octets. Add two bytes to account for // DER type and length information. if (bytes > 2 && bytes <= 22) { // Now that we know the size of the serial number allocate enough // space to hold it, and use i2d_ASN1_INTEGER() one last time to // hold it in the allocated buffer. unsigned char* serial_der = (unsigned char*) yr_malloc(bytes); if (serial_der != NULL) { unsigned char* serial_bytes; char* serial_ascii; bytes = i2d_ASN1_INTEGER(serial, &serial_der); // i2d_ASN1_INTEGER() moves the pointer as it writes into // serial_bytes. Move it back. serial_der -= bytes; // Skip over DER type, length information serial_bytes = serial_der + 2; bytes -= 2; // Also allocate space to hold the "common" string format: // 00:01:02:03:04... // // For each byte in the serial to convert to hexlified format we // need three bytes, two for the byte itself and one for colon. // The last one doesn't have the colon, but the extra byte is used // for the NULL terminator. serial_ascii = (char*) yr_malloc(bytes * 3); if (serial_ascii) { for (j = 0; j < bytes; j++) { // Don't put the colon on the last one. if (j < bytes - 1) snprintf(serial_ascii + 3 * j, 4, "%02x:", serial_bytes[j]); else snprintf(serial_ascii + 3 * j, 3, "%02x", serial_bytes[j]); } set_string( serial_ascii, pe->object, "signatures[%i].serial", *counter); yr_free(serial_ascii); } yr_free(serial_der); } } } date_time = ASN1_get_time_t(X509_get0_notBefore(cert)); set_integer(date_time, pe->object, "signatures[%i].not_before", *counter); date_time = ASN1_get_time_t(X509_get0_notAfter(cert)); set_integer(date_time, pe->object, "signatures[%i].not_after", *counter); (*counter)++; } // See if there is a nested signature, which is apparently an authenticode // specific feature. See https://github.com/VirusTotal/yara/issues/515. signer_info = sk_PKCS7_SIGNER_INFO_value(pkcs7->d.sign->signer_info, 0); if (signer_info != NULL) { attrs = PKCS7_get_attributes(signer_info); idx = X509at_get_attr_by_NID( attrs, OBJ_txt2nid(SPC_NESTED_SIGNATURE_OBJID), -1); xa = X509at_get_attr(attrs, idx); for (j = 0; j < MAX_PE_CERTS; j++) { nested = X509_ATTRIBUTE_get0_type(xa, j); if (nested == NULL) break; value = nested->value.sequence; p = value->data; nested_pkcs7 = d2i_PKCS7(NULL, &p, value->length); if (nested_pkcs7 != NULL) { _parse_pkcs7(pe, nested_pkcs7, counter); PKCS7_free(nested_pkcs7); } } } sk_X509_free(certs); } static void pe_parse_certificates(PE* pe) { int counter = 0; const uint8_t* eod; const unsigned char* cert_p; uintptr_t end; PWIN_CERTIFICATE win_cert; PIMAGE_DATA_DIRECTORY directory = pe_get_directory_entry( pe, IMAGE_DIRECTORY_ENTRY_SECURITY); if (directory == NULL) return; // Default to 0 signatures until we know otherwise. set_integer(0, pe->object, "number_of_signatures"); // directory->VirtualAddress is a file offset. Don't call pe_rva_to_offset(). if (yr_le32toh(directory->VirtualAddress) == 0 || yr_le32toh(directory->VirtualAddress) > pe->data_size || yr_le32toh(directory->Size) > pe->data_size || yr_le32toh(directory->VirtualAddress) + yr_le32toh(directory->Size) > pe->data_size) { return; } // Store the end of directory, making comparisons easier. eod = pe->data + yr_le32toh(directory->VirtualAddress) + yr_le32toh(directory->Size); win_cert = (PWIN_CERTIFICATE)( pe->data + yr_le32toh(directory->VirtualAddress)); // // Walk the directory, pulling out certificates. // // Make sure WIN_CERTIFICATE fits within the directory. // Make sure the Length specified fits within directory too. // // The docs say that the length is only for the Certificate, but the next // paragraph contradicts that. All the binaries I've seen have the Length // being the entire structure (Certificate included). // while (struct_fits_in_pe(pe, win_cert, WIN_CERTIFICATE) && yr_le32toh(win_cert->Length) > sizeof(WIN_CERTIFICATE) && fits_in_pe(pe, win_cert, yr_le32toh(win_cert->Length)) && (uint8_t*) win_cert + sizeof(WIN_CERTIFICATE) < eod && (uint8_t*) win_cert + yr_le32toh(win_cert->Length) <= eod) { PKCS7* pkcs7; // Some sanity checks if (yr_le32toh(win_cert->Length) == 0 || (yr_le16toh(win_cert->Revision) != WIN_CERT_REVISION_1_0 && yr_le16toh(win_cert->Revision) != WIN_CERT_REVISION_2_0)) { break; } // Don't support legacy revision for now. // Make sure type is PKCS#7 too. if (yr_le16toh(win_cert->Revision) != WIN_CERT_REVISION_2_0 || yr_le16toh(win_cert->CertificateType) != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { end = (uintptr_t)((uint8_t*) win_cert) + yr_le32toh(win_cert->Length); // Next certificate is aligned to the next 8-bytes boundary. win_cert = (PWIN_CERTIFICATE)((end + 7) & -8); continue; } cert_p = win_cert->Certificate; end = (uintptr_t)((uint8_t*) win_cert) + yr_le32toh(win_cert->Length); while ((uintptr_t) cert_p < end && counter < MAX_PE_CERTS) { pkcs7 = d2i_PKCS7(NULL, &cert_p, (uint32_t)(end - (uintptr_t) cert_p)); if (pkcs7 == NULL) break; _parse_pkcs7(pe, pkcs7, &counter); PKCS7_free(pkcs7); pkcs7 = NULL; } // Next certificate is aligned to the next 8-bytes boundary. win_cert = (PWIN_CERTIFICATE)((end + 7) & -8); } set_integer(counter, pe->object, "number_of_signatures"); } #endif // defined(HAVE_LIBCRYPTO) static void pe_parse_header(PE* pe, uint64_t base_address, int flags) { PIMAGE_SECTION_HEADER section; PIMAGE_DATA_DIRECTORY data_dir; char section_name[IMAGE_SIZEOF_SHORT_NAME + 1]; int scount, ddcount; uint64_t highest_sec_siz = 0; uint64_t highest_sec_ofs = 0; uint64_t section_end; uint64_t last_section_end; set_integer(1, pe->object, "is_pe"); set_integer( yr_le16toh(pe->header->FileHeader.Machine), pe->object, "machine"); set_integer( yr_le16toh(pe->header->FileHeader.NumberOfSections), pe->object, "number_of_sections"); set_integer( yr_le32toh(pe->header->FileHeader.TimeDateStamp), pe->object, "timestamp"); set_integer( yr_le32toh(pe->header->FileHeader.PointerToSymbolTable), pe->object, "pointer_to_symbol_table"); set_integer( yr_le32toh(pe->header->FileHeader.NumberOfSymbols), pe->object, "number_of_symbols"); set_integer( yr_le32toh(pe->header->FileHeader.SizeOfOptionalHeader), pe->object, "size_of_optional_header"); set_integer( yr_le16toh(pe->header->FileHeader.Characteristics), pe->object, "characteristics"); set_integer( flags & SCAN_FLAGS_PROCESS_MEMORY ? base_address + yr_le32toh(OptionalHeader(pe, AddressOfEntryPoint)) : pe_rva_to_offset( pe, yr_le32toh(OptionalHeader(pe, AddressOfEntryPoint))), pe->object, "entry_point"); set_integer( yr_le32toh(OptionalHeader(pe, AddressOfEntryPoint)), pe->object, "entry_point_raw"); set_integer( IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, ImageBase)) : yr_le32toh(OptionalHeader(pe, ImageBase)), pe->object, "image_base"); set_integer( yr_le32toh(OptionalHeader(pe, NumberOfRvaAndSizes)), pe->object, "number_of_rva_and_sizes"); set_integer( yr_le32toh(OptionalHeader(pe, Magic)), pe->object, "opthdr_magic"); set_integer( OptionalHeader(pe, MajorLinkerVersion), pe->object, "linker_version.major"); set_integer( OptionalHeader(pe, MinorLinkerVersion), pe->object, "linker_version.minor"); set_integer( yr_le32toh(OptionalHeader(pe, SizeOfCode)), pe->object, "size_of_code"); set_integer( yr_le32toh(OptionalHeader(pe, SizeOfInitializedData)), pe->object, "size_of_initialized_data"); set_integer( yr_le32toh(OptionalHeader(pe, SizeOfUninitializedData)), pe->object, "size_of_uninitialized_data"); set_integer( yr_le32toh(OptionalHeader(pe, BaseOfCode)), pe->object, "base_of_code"); if (!IS_64BITS_PE(pe)) { set_integer( yr_le32toh(pe->header->OptionalHeader.BaseOfData), pe->object, "base_of_data"); } set_integer( yr_le32toh(OptionalHeader(pe, SectionAlignment)), pe->object, "section_alignment"); set_integer( yr_le32toh(OptionalHeader(pe, FileAlignment)), pe->object, "file_alignment"); set_integer( yr_le16toh(OptionalHeader(pe, MajorOperatingSystemVersion)), pe->object, "os_version.major"); set_integer( yr_le16toh(OptionalHeader(pe, MinorOperatingSystemVersion)), pe->object, "os_version.minor"); set_integer( yr_le16toh(OptionalHeader(pe, MajorImageVersion)), pe->object, "image_version.major"); set_integer( yr_le16toh(OptionalHeader(pe, MinorImageVersion)), pe->object, "image_version.minor"); set_integer( yr_le16toh(OptionalHeader(pe, MajorSubsystemVersion)), pe->object, "subsystem_version.major"); set_integer( yr_le16toh(OptionalHeader(pe, MinorSubsystemVersion)), pe->object, "subsystem_version.minor"); set_integer( yr_le32toh(OptionalHeader(pe, Win32VersionValue)), pe->object, "win32_version_value"); set_integer( yr_le32toh(OptionalHeader(pe, SizeOfImage)), pe->object, "size_of_image"); set_integer( yr_le32toh(OptionalHeader(pe, SizeOfHeaders)), pe->object, "size_of_headers"); set_integer(yr_le32toh(OptionalHeader(pe, CheckSum)), pe->object, "checksum"); set_integer( yr_le16toh(OptionalHeader(pe, Subsystem)), pe->object, "subsystem"); set_integer( OptionalHeader(pe, DllCharacteristics), pe->object, "dll_characteristics"); set_integer( IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfStackReserve)) : yr_le32toh(OptionalHeader(pe, SizeOfStackReserve)), pe->object, "size_of_stack_reserve"); set_integer( IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfStackCommit)) : yr_le32toh(OptionalHeader(pe, SizeOfStackCommit)), pe->object, "size_of_stack_commit"); set_integer( IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfHeapReserve)) : yr_le32toh(OptionalHeader(pe, SizeOfHeapReserve)), pe->object, "size_of_heap_reserve"); set_integer( IS_64BITS_PE(pe) ? yr_le64toh(OptionalHeader(pe, SizeOfHeapCommit)) : yr_le32toh(OptionalHeader(pe, SizeOfHeapCommit)), pe->object, "size_of_heap_commit"); set_integer( yr_le32toh(OptionalHeader(pe, LoaderFlags)), pe->object, "loader_flags"); data_dir = IS_64BITS_PE(pe) ? pe->header64->OptionalHeader.DataDirectory : pe->header->OptionalHeader.DataDirectory; ddcount = yr_le16toh(OptionalHeader(pe, NumberOfRvaAndSizes)); ddcount = yr_min(ddcount, IMAGE_NUMBEROF_DIRECTORY_ENTRIES); for (int i = 0; i < ddcount; i++) { if (!struct_fits_in_pe(pe, data_dir, IMAGE_DATA_DIRECTORY)) break; set_integer( yr_le32toh(data_dir->VirtualAddress), pe->object, "data_directories[%i].virtual_address", i); set_integer( yr_le32toh(data_dir->Size), pe->object, "data_directories[%i].size", i); data_dir++; } pe_iterate_resources( pe, (RESOURCE_CALLBACK_FUNC) pe_collect_resources, (void*) pe); set_integer(pe->resources, pe->object, "number_of_resources"); section = IMAGE_FIRST_SECTION(pe->header); scount = yr_min( yr_le16toh(pe->header->FileHeader.NumberOfSections), MAX_PE_SECTIONS); for (int i = 0; i < scount; i++) { if (!struct_fits_in_pe(pe, section, IMAGE_SECTION_HEADER)) break; strncpy(section_name, (char*) section->Name, IMAGE_SIZEOF_SHORT_NAME); section_name[IMAGE_SIZEOF_SHORT_NAME] = '\0'; set_string(section_name, pe->object, "sections[%i].name", i); set_integer( yr_le32toh(section->Characteristics), pe->object, "sections[%i].characteristics", i); set_integer( yr_le32toh(section->SizeOfRawData), pe->object, "sections[%i].raw_data_size", i); set_integer( yr_le32toh(section->PointerToRawData), pe->object, "sections[%i].raw_data_offset", i); set_integer( yr_le32toh(section->VirtualAddress), pe->object, "sections[%i].virtual_address", i); set_integer( yr_le32toh(section->Misc.VirtualSize), pe->object, "sections[%i].virtual_size", i); set_integer( yr_le32toh(section->PointerToRelocations), pe->object, "sections[%i].pointer_to_relocations", i); set_integer( yr_le32toh(section->PointerToLinenumbers), pe->object, "sections[%i].pointer_to_line_numbers", i); set_integer( yr_le32toh(section->NumberOfRelocations), pe->object, "sections[%i].number_of_relocations", i); set_integer( yr_le32toh(section->NumberOfLinenumbers), pe->object, "sections[%i].number_of_line_numbers", i); // This will catch the section with the highest raw offset to help checking // if overlay data is present. If two sections have the same raw pointer // but different raw sizes the largest one is used. An example of this case // is file: cf62bf1815a93e68e6c5189f689286b66c4088b9507cf3ecf835e4ac3f9ededa section_end = yr_le32toh(section->PointerToRawData) + yr_le32toh(section->SizeOfRawData); if (section_end > highest_sec_ofs + highest_sec_siz) { highest_sec_ofs = yr_le32toh(section->PointerToRawData); highest_sec_siz = yr_le32toh(section->SizeOfRawData); } section++; } // An overlay is data appended to a PE file. Its location is at // RawData + RawOffset of the last section on the physical file last_section_end = highest_sec_siz + highest_sec_ofs; // For PE files that have overlaid data overlay.offset contains the offset // within the file where the overlay starts and overlay.size contains the // size. If the PE file doesn't have an overlay both fields are 0, if the // file is not a PE file (or is a malformed PE) both fields are YR_UNDEFINED. if (last_section_end && (pe->data_size > last_section_end)) { set_integer(last_section_end, pe->object, "overlay.offset"); set_integer(pe->data_size - last_section_end, pe->object, "overlay.size"); } else { set_integer(0, pe->object, "overlay.offset"); set_integer(0, pe->object, "overlay.size"); } } // // Given a posix timestamp argument, make sure not_before <= arg <= not_after // define_function(valid_on) { int64_t timestamp; int64_t not_before; int64_t not_after; if (is_undefined(parent(), "not_before") || is_undefined(parent(), "not_after")) { return_integer(YR_UNDEFINED); } timestamp = integer_argument(1); not_before = get_integer(parent(), "not_before"); not_after = get_integer(parent(), "not_after"); return_integer(timestamp >= not_before && timestamp <= not_after); } define_function(section_index_addr) { YR_OBJECT* module = module(); YR_SCAN_CONTEXT* context = scan_context(); int64_t offset; int64_t size; int64_t addr = integer_argument(1); int64_t n = get_integer(module, "number_of_sections"); if (is_undefined(module, "number_of_sections")) return_integer(YR_UNDEFINED); for (int i = 0; i < yr_min(n, MAX_PE_SECTIONS); i++) { if (context->flags & SCAN_FLAGS_PROCESS_MEMORY) { offset = get_integer(module, "sections[%i].virtual_address", i); size = get_integer(module, "sections[%i].virtual_size", i); } else { offset = get_integer(module, "sections[%i].raw_data_offset", i); size = get_integer(module, "sections[%i].raw_data_size", i); } if (addr >= offset && addr < offset + size) return_integer(i); } return_integer(YR_UNDEFINED); } define_function(section_index_name) { YR_OBJECT* module = module(); char* name = string_argument(1); int64_t n = get_integer(module, "number_of_sections"); if (is_undefined(module, "number_of_sections")) return_integer(YR_UNDEFINED); for (int i = 0; i < yr_min(n, MAX_PE_SECTIONS); i++) { SIZED_STRING* sect = get_string(module, "sections[%i].name", i); if (sect != NULL && strcmp(name, sect->c_string) == 0) return_integer(i); } return_integer(YR_UNDEFINED); } define_function(exports) { SIZED_STRING* search_name = sized_string_argument(1); SIZED_STRING* function_name = NULL; YR_OBJECT* module = module(); PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (pe == NULL) return_integer(YR_UNDEFINED); // If PE, but no exported functions, return false. int n = (int) get_integer(module, "number_of_exports"); if (n == 0) return_integer(0); for (int i = 0; i < n; i++) { function_name = get_string(module, "export_details[%i].name", i); if (function_name == NULL) continue; if (ss_icompare(function_name, search_name) == 0) return_integer(1); } return_integer(0); } define_function(exports_regexp) { RE* regex = regexp_argument(1); SIZED_STRING* function_name = NULL; YR_OBJECT* module = module(); PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (pe == NULL) return_integer(YR_UNDEFINED); // If PE, but no exported functions, return false. int n = (int) get_integer(module, "number_of_exports"); if (n == 0) return_integer(0); for (int i = 0; i < n; i++) { function_name = get_string(module, "export_details[%i].name", i); if (function_name == NULL) continue; if (yr_re_match(scan_context(), regex, function_name->c_string) != -1) return_integer(1); } return_integer(0); } define_function(exports_ordinal) { int64_t ordinal = integer_argument(1); YR_OBJECT* module = module(); PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (pe == NULL) return_integer(YR_UNDEFINED); // If PE, but no exported functions, return false. int n = (int) get_integer(module, "number_of_exports"); if (n == 0) return_integer(0); if (ordinal == 0 || ordinal > n) return_integer(0); for (int i = 0; i < n; i++) { int64_t exported_ordinal = yr_object_get_integer( module, "export_details[%i].ordinal", i); if (exported_ordinal == ordinal) return_integer(1); } return_integer(0); } define_function(exports_index_name) { SIZED_STRING* search_name = sized_string_argument(1); SIZED_STRING* function_name = NULL; YR_OBJECT* module = module(); PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (pe == NULL) return_integer(YR_UNDEFINED); // If PE, but no exported functions, return false. int n = (int) get_integer(module, "number_of_exports"); if (n == 0) return_integer(YR_UNDEFINED); for (int i = 0; i < n; i++) { function_name = get_string(module, "export_details[%i].name", i); if (function_name == NULL) continue; if (ss_icompare(function_name, search_name) == 0) return_integer(i); } return_integer(YR_UNDEFINED); } define_function(exports_index_ordinal) { int64_t ordinal = integer_argument(1); YR_OBJECT* module = module(); PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (pe == NULL) return_integer(YR_UNDEFINED); // If PE, but no exported functions, return false. int n = (int) get_integer(module, "number_of_exports"); if (n == 0) return_integer(YR_UNDEFINED); if (ordinal == 0 || ordinal > n) return_integer(YR_UNDEFINED); for (int i = 0; i < n; i++) { int64_t exported_ordinal = yr_object_get_integer( module, "export_details[%i].ordinal", i); if (exported_ordinal == ordinal) return_integer(i); } return_integer(YR_UNDEFINED); } define_function(exports_index_regex) { RE* regex = regexp_argument(1); SIZED_STRING* function_name = NULL; YR_OBJECT* module = module(); PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (pe == NULL) return_integer(YR_UNDEFINED); // If PE, but no exported functions, return false. int n = (int) get_integer(module, "number_of_exports"); if (n == 0) return_integer(YR_UNDEFINED); for (int i = 0; i < n; i++) { function_name = get_string(module, "export_details[%i].name", i); if (function_name == NULL) continue; if (yr_re_match(scan_context(), regex, function_name->c_string) != -1) { return_integer(i); } } return_integer(YR_UNDEFINED); } #if defined(HAVE_LIBCRYPTO) || defined(HAVE_WINCRYPT_H) || \ defined(HAVE_COMMONCRYPTO_COMMONCRYPTO_H) // // Generate an import hash: // https://www.mandiant.com/blog/tracking-malware-import-hashing/ // It is important to make duplicates of the strings as we don't want // to alter the contents of the parsed import structures. // define_function(imphash) { YR_OBJECT* module = module(); IMPORTED_DLL* dll; yr_md5_ctx ctx; unsigned char digest[YR_MD5_LEN]; char* digest_ascii; size_t i; bool first = true; PE* pe = (PE*) module->data; // If not a PE, return YR_UNDEFINED. if (!pe) return_string(YR_UNDEFINED); // Lookup in cache first. digest_ascii = (char*) yr_hash_table_lookup(pe->hash_table, "imphash", NULL); if (digest_ascii != NULL) return_string(digest_ascii); yr_md5_init(&ctx); dll = pe->imported_dlls; while (dll) { IMPORT_FUNCTION* func; size_t dll_name_len; char* dll_name; // If extension is 'ocx', 'sys' or 'dll', chop it. char* ext = strstr(dll->name, "."); if (ext && (strncasecmp(ext, ".ocx", 4) == 0 || strncasecmp(ext, ".sys", 4) == 0 || strncasecmp(ext, ".dll", 4) == 0)) { dll_name_len = (ext - dll->name); } else { dll_name_len = strlen(dll->name); } // Allocate a new string to hold the dll name. dll_name = (char*) yr_malloc(dll_name_len + 1); if (!dll_name) return ERROR_INSUFFICIENT_MEMORY; strlcpy(dll_name, dll->name, dll_name_len + 1); func = dll->functions; while (func) { char* final_name; size_t final_name_len = dll_name_len + strlen(func->name) + 1; if (!first) final_name_len++; // Additional byte to accommodate the extra comma final_name = (char*) yr_malloc(final_name_len + 1); if (final_name == NULL) break; sprintf(final_name, first ? "%s.%s" : ",%s.%s", dll_name, func->name); // Lowercase the whole thing. for (i = 0; i < final_name_len; i++) final_name[i] = tolower(final_name[i]); yr_md5_update(&ctx, final_name, final_name_len); yr_free(final_name); func = func->next; first = false; } yr_free(dll_name); dll = dll->next; } yr_md5_final(digest, &ctx); digest_ascii = (char*) yr_malloc(YR_MD5_LEN * 2 + 1); if (digest_ascii == NULL) return ERROR_INSUFFICIENT_MEMORY; // Transform the binary digest to ascii for (i = 0; i < YR_MD5_LEN; i++) { sprintf(digest_ascii + (i * 2), "%02x", digest[i]); } digest_ascii[YR_MD5_LEN * 2] = '\0'; yr_hash_table_add(pe->hash_table, "imphash", NULL, digest_ascii); return_string(digest_ascii); } #endif // defined(HAVE_LIBCRYPTO) || defined(HAVE_WINCRYPT_H) define_function(imports) { char* dll_name = string_argument(1); char* function_name = string_argument(2); YR_OBJECT* module = module(); PE* pe = (PE*) module->data; IMPORTED_DLL* imported_dll; if (!pe) return_integer(YR_UNDEFINED); imported_dll = pe->imported_dlls; while (imported_dll != NULL) { if (strcasecmp(imported_dll->name, dll_name) == 0) { IMPORT_FUNCTION* imported_func = imported_dll->functions; while (imported_func != NULL) { if (imported_func->name && strcasecmp(imported_func->name, function_name) == 0) return_integer(1); imported_func = imported_func->next; } } imported_dll = imported_dll->next; } return_integer(0); } define_function(imports_ordinal) { char* dll_name = string_argument(1); int64_t ordinal = integer_argument(2); YR_OBJECT* module = module(); PE* pe = (PE*) module->data; IMPORTED_DLL* imported_dll; if (!pe) return_integer(YR_UNDEFINED); imported_dll = pe->imported_dlls; while (imported_dll != NULL) { if (strcasecmp(imported_dll->name, dll_name) == 0) { IMPORT_FUNCTION* imported_func = imported_dll->functions; while (imported_func != NULL) { if (imported_func->has_ordinal && imported_func->ordinal == ordinal) return_integer(1); imported_func = imported_func->next; } } imported_dll = imported_dll->next; } return_integer(0); } define_function(imports_regex) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; IMPORTED_DLL* imported_dll; uint64_t imported_func_count = 0; if (!pe) return_integer(YR_UNDEFINED); imported_dll = pe->imported_dlls; while (imported_dll != NULL) { if (yr_re_match(scan_context(), regexp_argument(1), imported_dll->name) > 0) { IMPORT_FUNCTION* imported_func = imported_dll->functions; while (imported_func != NULL) { if (yr_re_match( scan_context(), regexp_argument(2), imported_func->name) > 0) imported_func_count++; imported_func = imported_func->next; } } imported_dll = imported_dll->next; } return_integer(imported_func_count); } define_function(imports_dll) { char* dll_name = string_argument(1); YR_OBJECT* module = module(); PE* pe = (PE*) module->data; IMPORTED_DLL* imported_dll; uint64_t imported_func_count = 0; if (!pe) return_integer(YR_UNDEFINED); imported_dll = pe->imported_dlls; while (imported_dll != NULL) { if (strcasecmp(imported_dll->name, dll_name) == 0) { IMPORT_FUNCTION* imported_func = imported_dll->functions; while (imported_func != NULL) { imported_func_count++; imported_func = imported_func->next; } } imported_dll = imported_dll->next; } return_integer(imported_func_count); } define_function(locale) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; uint64_t locale = integer_argument(1); if (is_undefined(module, "number_of_resources")) return_integer(YR_UNDEFINED); // If not a PE file, return YR_UNDEFINED if (pe == NULL) return_integer(YR_UNDEFINED); int n = (int) get_integer(module, "number_of_resources"); for (int i = 0; i < n; i++) { uint64_t rsrc_language = get_integer(module, "resources[%i].language", i); if ((rsrc_language & 0xFFFF) == locale) return_integer(1); } return_integer(0); } define_function(language) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; uint64_t language = integer_argument(1); if (is_undefined(module, "number_of_resources")) return_integer(YR_UNDEFINED); // If not a PE file, return YR_UNDEFINED if (pe == NULL) return_integer(YR_UNDEFINED); int n = (int) get_integer(module, "number_of_resources"); for (int i = 0; i < n; i++) { uint64_t rsrc_language = get_integer(module, "resources[%i].language", i); if ((rsrc_language & 0xFF) == language) return_integer(1); } return_integer(0); } define_function(is_dll) { int64_t characteristics; YR_OBJECT* module = module(); if (is_undefined(module, "characteristics")) return_integer(YR_UNDEFINED); characteristics = get_integer(module, "characteristics"); return_integer(characteristics & IMAGE_FILE_DLL); } define_function(is_32bit) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; if (pe == NULL) return_integer(YR_UNDEFINED); return_integer(IS_64BITS_PE(pe) ? 0 : 1); } define_function(is_64bit) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; if (pe == NULL) return_integer(YR_UNDEFINED); return_integer(IS_64BITS_PE(pe) ? 1 : 0); } // _rich_version // // Returns the number of rich signatures that match the specified version and // toolid numbers. // static uint64_t _rich_version( YR_OBJECT* module, uint64_t version, uint64_t toolid) { int64_t rich_length; int64_t rich_count; PRICH_SIGNATURE clear_rich_signature; SIZED_STRING* rich_string; uint64_t result = 0; // Check if the required fields are set if (is_undefined(module, "rich_signature.length")) return YR_UNDEFINED; rich_length = get_integer(module, "rich_signature.length"); rich_string = get_string(module, "rich_signature.clear_data"); // If the clear_data was not set, return YR_UNDEFINED if (rich_string == NULL) return YR_UNDEFINED; if (version == YR_UNDEFINED && toolid == YR_UNDEFINED) return false; clear_rich_signature = (PRICH_SIGNATURE) rich_string->c_string; // Loop over the versions in the rich signature rich_count = (rich_length - sizeof(RICH_SIGNATURE)) / sizeof(RICH_VERSION_INFO); for (int i = 0; i < rich_count; i++) { DWORD id_version = yr_le32toh(clear_rich_signature->versions[i].id_version); int match_version = (version == RICH_VERSION_VERSION(id_version)); int match_toolid = (toolid == RICH_VERSION_ID(id_version)); if ((version == YR_UNDEFINED || match_version) && (toolid == YR_UNDEFINED || match_toolid)) { result += yr_le32toh(clear_rich_signature->versions[i].times); } } return result; } define_function(rich_version) { return_integer(_rich_version(module(), integer_argument(1), YR_UNDEFINED)); } define_function(rich_version_toolid) { return_integer( _rich_version(module(), integer_argument(1), integer_argument(2))); } define_function(rich_toolid) { return_integer(_rich_version(module(), YR_UNDEFINED, integer_argument(1))); } define_function(rich_toolid_version) { return_integer( _rich_version(module(), integer_argument(2), integer_argument(1))); } define_function(calculate_checksum) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; uint64_t csum = 0; size_t csum_offset; if (pe == NULL) return_integer(YR_UNDEFINED); csum_offset = ((uint8_t*) &(pe->header->OptionalHeader) + offsetof(IMAGE_OPTIONAL_HEADER32, CheckSum)) - pe->data; for (size_t i = 0; i <= pe->data_size / 4; i++) { // Treat the CheckSum field as 0 -- the offset is the same for // PE32 and PE64. if (4 * i == csum_offset) continue; if (4 * i + 4 <= pe->data_size) { csum += ((uint64_t) pe->data[4 * i] + ((uint64_t) pe->data[4 * i + 1] << 8) + ((uint64_t) pe->data[4 * i + 2] << 16) + ((uint64_t) pe->data[4 * i + 3] << 24)); } else { for (size_t j = 0; j < pe->data_size % 4; j++) csum += (uint64_t) pe->data[4 * i + j] << (8 * j); } if (csum > 0xffffffff) csum = (csum & 0xffffffff) + (csum >> 32); } csum = (csum & 0xffff) + (csum >> 16); csum += (csum >> 16); csum &= 0xffff; csum += pe->data_size; return_integer(csum); } define_function(rva_to_offset) { YR_OBJECT* module = module(); PE* pe = (PE*) module->data; uint64_t rva; int64_t offset; if (pe == NULL) return_integer(YR_UNDEFINED); rva = integer_argument(1); offset = pe_rva_to_offset(pe, rva); if (offset == -1) return_integer(YR_UNDEFINED); return_integer(offset); } begin_declarations declare_integer("MACHINE_UNKNOWN"); declare_integer("MACHINE_AM33"); declare_integer("MACHINE_AMD64"); declare_integer("MACHINE_ARM"); declare_integer("MACHINE_ARMNT"); declare_integer("MACHINE_ARM64"); declare_integer("MACHINE_EBC"); declare_integer("MACHINE_I386"); declare_integer("MACHINE_IA64"); declare_integer("MACHINE_M32R"); declare_integer("MACHINE_MIPS16"); declare_integer("MACHINE_MIPSFPU"); declare_integer("MACHINE_MIPSFPU16"); declare_integer("MACHINE_POWERPC"); declare_integer("MACHINE_POWERPCFP"); declare_integer("MACHINE_R4000"); declare_integer("MACHINE_SH3"); declare_integer("MACHINE_SH3DSP"); declare_integer("MACHINE_SH4"); declare_integer("MACHINE_SH5"); declare_integer("MACHINE_THUMB"); declare_integer("MACHINE_WCEMIPSV2"); declare_integer("SUBSYSTEM_UNKNOWN"); declare_integer("SUBSYSTEM_NATIVE"); declare_integer("SUBSYSTEM_WINDOWS_GUI"); declare_integer("SUBSYSTEM_WINDOWS_CUI"); declare_integer("SUBSYSTEM_OS2_CUI"); declare_integer("SUBSYSTEM_POSIX_CUI"); declare_integer("SUBSYSTEM_NATIVE_WINDOWS"); declare_integer("SUBSYSTEM_WINDOWS_CE_GUI"); declare_integer("SUBSYSTEM_EFI_APPLICATION"); declare_integer("SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER"); declare_integer("SUBSYSTEM_EFI_RUNTIME_DRIVER"); declare_integer("SUBSYSTEM_XBOX"); declare_integer("SUBSYSTEM_WINDOWS_BOOT_APPLICATION"); declare_integer("DYNAMIC_BASE"); declare_integer("FORCE_INTEGRITY"); declare_integer("NX_COMPAT"); declare_integer("NO_ISOLATION"); declare_integer("NO_SEH"); declare_integer("NO_BIND"); declare_integer("WDM_DRIVER"); declare_integer("TERMINAL_SERVER_AWARE"); declare_integer("RELOCS_STRIPPED"); declare_integer("EXECUTABLE_IMAGE"); declare_integer("LINE_NUMS_STRIPPED"); declare_integer("LOCAL_SYMS_STRIPPED"); declare_integer("AGGRESIVE_WS_TRIM"); declare_integer("LARGE_ADDRESS_AWARE"); declare_integer("BYTES_REVERSED_LO"); declare_integer("MACHINE_32BIT"); declare_integer("DEBUG_STRIPPED"); declare_integer("REMOVABLE_RUN_FROM_SWAP"); declare_integer("NET_RUN_FROM_SWAP"); declare_integer("SYSTEM"); declare_integer("DLL"); declare_integer("UP_SYSTEM_ONLY"); declare_integer("BYTES_REVERSED_HI"); declare_integer("IMAGE_DIRECTORY_ENTRY_EXPORT"); declare_integer("IMAGE_DIRECTORY_ENTRY_IMPORT"); declare_integer("IMAGE_DIRECTORY_ENTRY_RESOURCE"); declare_integer("IMAGE_DIRECTORY_ENTRY_EXCEPTION"); declare_integer("IMAGE_DIRECTORY_ENTRY_SECURITY"); declare_integer("IMAGE_DIRECTORY_ENTRY_BASERELOC"); declare_integer("IMAGE_DIRECTORY_ENTRY_DEBUG"); declare_integer("IMAGE_DIRECTORY_ENTRY_ARCHITECTURE"); declare_integer("IMAGE_DIRECTORY_ENTRY_GLOBALPTR"); declare_integer("IMAGE_DIRECTORY_ENTRY_TLS"); declare_integer("IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG"); declare_integer("IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT"); declare_integer("IMAGE_DIRECTORY_ENTRY_IAT"); declare_integer("IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT"); declare_integer("IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR"); declare_integer("SECTION_CNT_CODE"); declare_integer("SECTION_CNT_INITIALIZED_DATA"); declare_integer("SECTION_CNT_UNINITIALIZED_DATA"); declare_integer("SECTION_GPREL"); declare_integer("SECTION_MEM_16BIT"); declare_integer("SECTION_LNK_NRELOC_OVFL"); declare_integer("SECTION_MEM_DISCARDABLE"); declare_integer("SECTION_MEM_NOT_CACHED"); declare_integer("SECTION_MEM_NOT_PAGED"); declare_integer("SECTION_MEM_SHARED"); declare_integer("SECTION_MEM_EXECUTE"); declare_integer("SECTION_MEM_READ"); declare_integer("SECTION_MEM_WRITE"); declare_integer("RESOURCE_TYPE_CURSOR"); declare_integer("RESOURCE_TYPE_BITMAP"); declare_integer("RESOURCE_TYPE_ICON"); declare_integer("RESOURCE_TYPE_MENU"); declare_integer("RESOURCE_TYPE_DIALOG"); declare_integer("RESOURCE_TYPE_STRING"); declare_integer("RESOURCE_TYPE_FONTDIR"); declare_integer("RESOURCE_TYPE_FONT"); declare_integer("RESOURCE_TYPE_ACCELERATOR"); declare_integer("RESOURCE_TYPE_RCDATA"); declare_integer("RESOURCE_TYPE_MESSAGETABLE"); declare_integer("RESOURCE_TYPE_GROUP_CURSOR"); declare_integer("RESOURCE_TYPE_GROUP_ICON"); declare_integer("RESOURCE_TYPE_VERSION"); declare_integer("RESOURCE_TYPE_DLGINCLUDE"); declare_integer("RESOURCE_TYPE_PLUGPLAY"); declare_integer("RESOURCE_TYPE_VXD"); declare_integer("RESOURCE_TYPE_ANICURSOR"); declare_integer("RESOURCE_TYPE_ANIICON"); declare_integer("RESOURCE_TYPE_HTML"); declare_integer("RESOURCE_TYPE_MANIFEST"); declare_integer("is_pe"); declare_integer("machine"); declare_integer("number_of_sections"); declare_integer("timestamp"); declare_integer("pointer_to_symbol_table"); declare_integer("number_of_symbols"); declare_integer("size_of_optional_header"); declare_integer("characteristics"); declare_integer("entry_point"); declare_integer("entry_point_raw"); declare_integer("image_base"); declare_integer("number_of_rva_and_sizes"); declare_string_dictionary("version_info"); declare_integer("opthdr_magic"); declare_integer("size_of_code"); declare_integer("size_of_initialized_data"); declare_integer("size_of_uninitialized_data"); declare_integer("base_of_code"); declare_integer("base_of_data"); declare_integer("section_alignment"); declare_integer("file_alignment"); begin_struct("linker_version") declare_integer("major"); declare_integer("minor"); end_struct("linker_version"); begin_struct("os_version") declare_integer("major"); declare_integer("minor"); end_struct("os_version"); begin_struct("image_version") declare_integer("major"); declare_integer("minor"); end_struct("image_version"); begin_struct("subsystem_version") declare_integer("major"); declare_integer("minor"); end_struct("subsystem_version"); declare_integer("win32_version_value"); declare_integer("size_of_image"); declare_integer("size_of_headers"); declare_integer("checksum"); declare_function("calculate_checksum", "", "i", calculate_checksum); declare_integer("subsystem"); declare_integer("dll_characteristics"); declare_integer("size_of_stack_reserve"); declare_integer("size_of_stack_commit"); declare_integer("size_of_heap_reserve"); declare_integer("size_of_heap_commit"); declare_integer("loader_flags"); begin_struct_array("data_directories") declare_integer("virtual_address"); declare_integer("size"); end_struct_array("data_directories"); begin_struct_array("sections") declare_string("name"); declare_integer("characteristics"); declare_integer("virtual_address"); declare_integer("virtual_size"); declare_integer("raw_data_offset"); declare_integer("raw_data_size"); declare_integer("pointer_to_relocations"); declare_integer("pointer_to_line_numbers"); declare_integer("number_of_relocations"); declare_integer("number_of_line_numbers"); end_struct_array("sections"); begin_struct("overlay") declare_integer("offset"); declare_integer("size"); end_struct("overlay"); begin_struct("rich_signature") declare_integer("offset"); declare_integer("length"); declare_integer("key"); declare_string("raw_data"); declare_string("clear_data"); declare_function("version", "i", "i", rich_version); declare_function("version", "ii", "i", rich_version_toolid); declare_function("toolid", "i", "i", rich_toolid); declare_function("toolid", "ii", "i", rich_toolid_version); end_struct("rich_signature"); #if defined(HAVE_LIBCRYPTO) || defined(HAVE_WINCRYPT_H) || \ defined(HAVE_COMMONCRYPTO_COMMONCRYPTO_H) declare_function("imphash", "", "s", imphash); #endif declare_function("section_index", "s", "i", section_index_name); declare_function("section_index", "i", "i", section_index_addr); declare_function("exports", "s", "i", exports); declare_function("exports", "r", "i", exports_regexp); declare_function("exports", "i", "i", exports_ordinal); declare_function("exports_index", "s", "i", exports_index_name); declare_function("exports_index", "i", "i", exports_index_ordinal); declare_function("exports_index", "r", "i", exports_index_regex); declare_function("imports", "ss", "i", imports); declare_function("imports", "si", "i", imports_ordinal); declare_function("imports", "s", "i", imports_dll); declare_function("imports", "rr", "i", imports_regex); declare_function("locale", "i", "i", locale); declare_function("language", "i", "i", language); declare_function("is_dll", "", "i", is_dll); declare_function("is_32bit", "", "i", is_32bit); declare_function("is_64bit", "", "i", is_64bit); declare_integer("number_of_imports"); declare_integer("number_of_imported_functions"); declare_integer("number_of_exports"); declare_string("dll_name"); declare_integer("export_timestamp"); begin_struct_array("export_details") declare_integer("offset"); declare_string("name"); declare_string("forward_name"); declare_integer("ordinal"); end_struct_array("export_details") declare_integer("resource_timestamp"); begin_struct("resource_version") declare_integer("major"); declare_integer("minor"); end_struct("resource_version") begin_struct_array("resources") declare_integer("rva"); declare_integer("offset"); declare_integer("length"); declare_integer("type"); declare_integer("id"); declare_integer("language"); declare_string("type_string"); declare_string("name_string"); declare_string("language_string"); end_struct_array("resources") declare_integer("number_of_resources"); declare_string("pdb_path"); #if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL) begin_struct_array("signatures") declare_string("thumbprint"); declare_string("issuer"); declare_string("subject"); declare_integer("version"); declare_string("algorithm"); declare_string("algorithm_oid"); declare_string("serial"); declare_integer("not_before"); declare_integer("not_after"); declare_function("valid_on", "i", "i", valid_on); end_struct_array("signatures") declare_integer("number_of_signatures"); #endif declare_function("rva_to_offset", "i", "i", rva_to_offset); end_declarations int module_initialize(YR_MODULE* module) { #if defined(HAVE_LIBCRYPTO) // Not checking return value here because if it fails we will not parse the // nested signature silently. OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL); #endif return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { YR_MEMORY_BLOCK* block; YR_MEMORY_BLOCK_ITERATOR* iterator = context->iterator; PIMAGE_NT_HEADERS32 pe_header; const uint8_t* block_data = NULL; PE* pe = NULL; set_integer(IMAGE_FILE_MACHINE_UNKNOWN, module_object, "MACHINE_UNKNOWN"); set_integer(IMAGE_FILE_MACHINE_AM33, module_object, "MACHINE_AM33"); set_integer(IMAGE_FILE_MACHINE_AMD64, module_object, "MACHINE_AMD64"); set_integer(IMAGE_FILE_MACHINE_ARM, module_object, "MACHINE_ARM"); set_integer(IMAGE_FILE_MACHINE_ARMNT, module_object, "MACHINE_ARMNT"); set_integer(IMAGE_FILE_MACHINE_ARM64, module_object, "MACHINE_ARM64"); set_integer(IMAGE_FILE_MACHINE_EBC, module_object, "MACHINE_EBC"); set_integer(IMAGE_FILE_MACHINE_I386, module_object, "MACHINE_I386"); set_integer(IMAGE_FILE_MACHINE_IA64, module_object, "MACHINE_IA64"); set_integer(IMAGE_FILE_MACHINE_M32R, module_object, "MACHINE_M32R"); set_integer(IMAGE_FILE_MACHINE_MIPS16, module_object, "MACHINE_MIPS16"); set_integer(IMAGE_FILE_MACHINE_MIPSFPU, module_object, "MACHINE_MIPSFPU"); set_integer(IMAGE_FILE_MACHINE_MIPSFPU16, module_object, "MACHINE_MIPSFPU16"); set_integer(IMAGE_FILE_MACHINE_POWERPC, module_object, "MACHINE_POWERPC"); set_integer(IMAGE_FILE_MACHINE_POWERPCFP, module_object, "MACHINE_POWERPCFP"); set_integer(IMAGE_FILE_MACHINE_R4000, module_object, "MACHINE_R4000"); set_integer(IMAGE_FILE_MACHINE_SH3, module_object, "MACHINE_SH3"); set_integer(IMAGE_FILE_MACHINE_SH3DSP, module_object, "MACHINE_SH3DSP"); set_integer(IMAGE_FILE_MACHINE_SH4, module_object, "MACHINE_SH4"); set_integer(IMAGE_FILE_MACHINE_SH5, module_object, "MACHINE_SH5"); set_integer(IMAGE_FILE_MACHINE_THUMB, module_object, "MACHINE_THUMB"); set_integer(IMAGE_FILE_MACHINE_WCEMIPSV2, module_object, "MACHINE_WCEMIPSV2"); set_integer(IMAGE_SUBSYSTEM_UNKNOWN, module_object, "SUBSYSTEM_UNKNOWN"); set_integer(IMAGE_SUBSYSTEM_NATIVE, module_object, "SUBSYSTEM_NATIVE"); set_integer( IMAGE_SUBSYSTEM_WINDOWS_GUI, module_object, "SUBSYSTEM_WINDOWS_GUI"); set_integer( IMAGE_SUBSYSTEM_WINDOWS_CUI, module_object, "SUBSYSTEM_WINDOWS_CUI"); set_integer(IMAGE_SUBSYSTEM_OS2_CUI, module_object, "SUBSYSTEM_OS2_CUI"); set_integer(IMAGE_SUBSYSTEM_POSIX_CUI, module_object, "SUBSYSTEM_POSIX_CUI"); set_integer( IMAGE_SUBSYSTEM_NATIVE_WINDOWS, module_object, "SUBSYSTEM_NATIVE_WINDOWS"); set_integer( IMAGE_SUBSYSTEM_WINDOWS_CE_GUI, module_object, "SUBSYSTEM_WINDOWS_CE_GUI"); set_integer( IMAGE_SUBSYSTEM_EFI_APPLICATION, module_object, "SUBSYSTEM_EFI_APPLICATION"); set_integer( IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER, module_object, "SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER"); set_integer( IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER, module_object, "SUBSYSTEM_EFI_RUNTIME_DRIVER"); set_integer(IMAGE_SUBSYSTEM_XBOX, module_object, "SUBSYSTEM_XBOX"); set_integer( IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION, module_object, "SUBSYSTEM_WINDOWS_BOOT_APPLICATION"); set_integer( IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE, module_object, "DYNAMIC_BASE"); set_integer( IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY, module_object, "FORCE_INTEGRITY"); set_integer(IMAGE_DLLCHARACTERISTICS_NX_COMPAT, module_object, "NX_COMPAT"); set_integer( IMAGE_DLLCHARACTERISTICS_NO_ISOLATION, module_object, "NO_ISOLATION"); set_integer(IMAGE_DLLCHARACTERISTICS_NO_SEH, module_object, "NO_SEH"); set_integer(IMAGE_DLLCHARACTERISTICS_NO_BIND, module_object, "NO_BIND"); set_integer(IMAGE_DLLCHARACTERISTICS_WDM_DRIVER, module_object, "WDM_DRIVER"); set_integer( IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE, module_object, "TERMINAL_SERVER_AWARE"); set_integer(IMAGE_FILE_RELOCS_STRIPPED, module_object, "RELOCS_STRIPPED"); set_integer(IMAGE_FILE_EXECUTABLE_IMAGE, module_object, "EXECUTABLE_IMAGE"); set_integer( IMAGE_FILE_LINE_NUMS_STRIPPED, module_object, "LINE_NUMS_STRIPPED"); set_integer( IMAGE_FILE_LOCAL_SYMS_STRIPPED, module_object, "LOCAL_SYMS_STRIPPED"); set_integer(IMAGE_FILE_AGGRESIVE_WS_TRIM, module_object, "AGGRESIVE_WS_TRIM"); set_integer( IMAGE_FILE_LARGE_ADDRESS_AWARE, module_object, "LARGE_ADDRESS_AWARE"); set_integer(IMAGE_FILE_BYTES_REVERSED_LO, module_object, "BYTES_REVERSED_LO"); set_integer(IMAGE_FILE_32BIT_MACHINE, module_object, "MACHINE_32BIT"); set_integer(IMAGE_FILE_DEBUG_STRIPPED, module_object, "DEBUG_STRIPPED"); set_integer( IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP, module_object, "REMOVABLE_RUN_FROM_SWAP"); set_integer(IMAGE_FILE_NET_RUN_FROM_SWAP, module_object, "NET_RUN_FROM_SWAP"); set_integer(IMAGE_FILE_SYSTEM, module_object, "SYSTEM"); set_integer(IMAGE_FILE_DLL, module_object, "DLL"); set_integer(IMAGE_FILE_UP_SYSTEM_ONLY, module_object, "UP_SYSTEM_ONLY"); set_integer(IMAGE_FILE_BYTES_REVERSED_HI, module_object, "BYTES_REVERSED_HI"); set_integer( IMAGE_DIRECTORY_ENTRY_EXPORT, module_object, "IMAGE_DIRECTORY_ENTRY_EXPORT"); set_integer( IMAGE_DIRECTORY_ENTRY_IMPORT, module_object, "IMAGE_DIRECTORY_ENTRY_IMPORT"); set_integer( IMAGE_DIRECTORY_ENTRY_RESOURCE, module_object, "IMAGE_DIRECTORY_ENTRY_RESOURCE"); set_integer( IMAGE_DIRECTORY_ENTRY_EXCEPTION, module_object, "IMAGE_DIRECTORY_ENTRY_EXCEPTION"); set_integer( IMAGE_DIRECTORY_ENTRY_SECURITY, module_object, "IMAGE_DIRECTORY_ENTRY_SECURITY"); set_integer( IMAGE_DIRECTORY_ENTRY_BASERELOC, module_object, "IMAGE_DIRECTORY_ENTRY_BASERELOC"); set_integer( IMAGE_DIRECTORY_ENTRY_DEBUG, module_object, "IMAGE_DIRECTORY_ENTRY_DEBUG"); set_integer( IMAGE_DIRECTORY_ENTRY_ARCHITECTURE, module_object, "IMAGE_DIRECTORY_ENTRY_ARCHITECTURE"); set_integer( IMAGE_DIRECTORY_ENTRY_GLOBALPTR, module_object, "IMAGE_DIRECTORY_ENTRY_GLOBALPTR"); set_integer( IMAGE_DIRECTORY_ENTRY_TLS, module_object, "IMAGE_DIRECTORY_ENTRY_TLS"); set_integer( IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, module_object, "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG"); set_integer( IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, module_object, "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT"); set_integer( IMAGE_DIRECTORY_ENTRY_IAT, module_object, "IMAGE_DIRECTORY_ENTRY_IAT"); set_integer( IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT, module_object, "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT"); set_integer( IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, module_object, "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR"); set_integer(IMAGE_SCN_CNT_CODE, module_object, "SECTION_CNT_CODE"); set_integer( IMAGE_SCN_CNT_INITIALIZED_DATA, module_object, "SECTION_CNT_INITIALIZED_DATA"); set_integer( IMAGE_SCN_CNT_UNINITIALIZED_DATA, module_object, "SECTION_CNT_UNINITIALIZED_DATA"); set_integer(IMAGE_SCN_GPREL, module_object, "SECTION_GPREL"); set_integer(IMAGE_SCN_MEM_16BIT, module_object, "SECTION_MEM_16BIT"); set_integer( IMAGE_SCN_LNK_NRELOC_OVFL, module_object, "SECTION_LNK_NRELOC_OVFL"); set_integer( IMAGE_SCN_MEM_DISCARDABLE, module_object, "SECTION_MEM_DISCARDABLE"); set_integer( IMAGE_SCN_MEM_NOT_CACHED, module_object, "SECTION_MEM_NOT_CACHED"); set_integer(IMAGE_SCN_MEM_NOT_PAGED, module_object, "SECTION_MEM_NOT_PAGED"); set_integer(IMAGE_SCN_MEM_SHARED, module_object, "SECTION_MEM_SHARED"); set_integer(IMAGE_SCN_MEM_EXECUTE, module_object, "SECTION_MEM_EXECUTE"); set_integer(IMAGE_SCN_MEM_READ, module_object, "SECTION_MEM_READ"); set_integer(IMAGE_SCN_MEM_WRITE, module_object, "SECTION_MEM_WRITE"); set_integer(RESOURCE_TYPE_CURSOR, module_object, "RESOURCE_TYPE_CURSOR"); set_integer(RESOURCE_TYPE_BITMAP, module_object, "RESOURCE_TYPE_BITMAP"); set_integer(RESOURCE_TYPE_ICON, module_object, "RESOURCE_TYPE_ICON"); set_integer(RESOURCE_TYPE_MENU, module_object, "RESOURCE_TYPE_MENU"); set_integer(RESOURCE_TYPE_DIALOG, module_object, "RESOURCE_TYPE_DIALOG"); set_integer(RESOURCE_TYPE_STRING, module_object, "RESOURCE_TYPE_STRING"); set_integer(RESOURCE_TYPE_FONTDIR, module_object, "RESOURCE_TYPE_FONTDIR"); set_integer(RESOURCE_TYPE_FONT, module_object, "RESOURCE_TYPE_FONT"); set_integer( RESOURCE_TYPE_ACCELERATOR, module_object, "RESOURCE_TYPE_ACCELERATOR"); set_integer(RESOURCE_TYPE_RCDATA, module_object, "RESOURCE_TYPE_RCDATA"); set_integer( RESOURCE_TYPE_MESSAGETABLE, module_object, "RESOURCE_TYPE_MESSAGETABLE"); set_integer( RESOURCE_TYPE_GROUP_CURSOR, module_object, "RESOURCE_TYPE_GROUP_CURSOR"); set_integer( RESOURCE_TYPE_GROUP_ICON, module_object, "RESOURCE_TYPE_GROUP_ICON"); set_integer(RESOURCE_TYPE_VERSION, module_object, "RESOURCE_TYPE_VERSION"); set_integer( RESOURCE_TYPE_DLGINCLUDE, module_object, "RESOURCE_TYPE_DLGINCLUDE"); set_integer(RESOURCE_TYPE_PLUGPLAY, module_object, "RESOURCE_TYPE_PLUGPLAY"); set_integer(RESOURCE_TYPE_VXD, module_object, "RESOURCE_TYPE_VXD"); set_integer( RESOURCE_TYPE_ANICURSOR, module_object, "RESOURCE_TYPE_ANICURSOR"); set_integer(RESOURCE_TYPE_ANIICON, module_object, "RESOURCE_TYPE_ANIICON"); set_integer(RESOURCE_TYPE_HTML, module_object, "RESOURCE_TYPE_HTML"); set_integer(RESOURCE_TYPE_MANIFEST, module_object, "RESOURCE_TYPE_MANIFEST"); set_integer(0, module_object, "is_pe"); foreach_memory_block(iterator, block) { block_data = block->fetch_data(block); if (block_data == NULL) continue; pe_header = pe_get_header(block_data, block->size); if (pe_header != NULL) { // Ignore DLLs while scanning a process if (!(context->flags & SCAN_FLAGS_PROCESS_MEMORY) || !(yr_le16toh(pe_header->FileHeader.Characteristics) & IMAGE_FILE_DLL)) { pe = (PE*) yr_malloc(sizeof(PE)); if (pe == NULL) return ERROR_INSUFFICIENT_MEMORY; FAIL_ON_ERROR_WITH_CLEANUP( yr_hash_table_create(17, &pe->hash_table), yr_free(pe)); pe->data = block_data; pe->data_size = block->size; pe->header = pe_header; pe->object = module_object; pe->resources = 0; module_object->data = pe; pe_parse_header(pe, block->base, context->flags); pe_parse_rich_signature(pe, block->base); pe_parse_debug_directory(pe); #if defined(HAVE_LIBCRYPTO) && !defined(BORINGSSL) pe_parse_certificates(pe); #endif pe->imported_dlls = pe_parse_imports(pe); pe_parse_exports(pe); break; } } } return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { IMPORTED_DLL* dll = NULL; IMPORTED_DLL* next_dll = NULL; IMPORT_FUNCTION* func = NULL; IMPORT_FUNCTION* next_func = NULL; PE* pe = (PE*) module_object->data; if (pe == NULL) return ERROR_SUCCESS; if (pe->hash_table != NULL) yr_hash_table_destroy( pe->hash_table, (YR_HASH_TABLE_FREE_VALUE_FUNC) yr_free); dll = pe->imported_dlls; while (dll) { if (dll->name) yr_free(dll->name); func = dll->functions; while (func) { if (func->name) yr_free(func->name); next_func = func->next; yr_free(func); func = next_func; } next_dll = dll->next; yr_free(dll); dll = next_dll; } yr_free(pe); return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/pe/pe_utils.c000066400000000000000000001302231413423160300201400ustar00rootroot00000000000000/* Copyright (c) 2014-2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #if HAVE_LIBCRYPTO #include #endif PIMAGE_NT_HEADERS32 pe_get_header(const uint8_t* data, size_t data_size) { PIMAGE_DOS_HEADER mz_header; PIMAGE_NT_HEADERS32 pe_header; size_t headers_size = 0; if (data_size < sizeof(IMAGE_DOS_HEADER)) return NULL; mz_header = (PIMAGE_DOS_HEADER) data; if (yr_le16toh(mz_header->e_magic) != IMAGE_DOS_SIGNATURE) return NULL; if (yr_le32toh(mz_header->e_lfanew) < 0) return NULL; headers_size = yr_le32toh(mz_header->e_lfanew) + sizeof(pe_header->Signature) + sizeof(IMAGE_FILE_HEADER); if (data_size < headers_size) return NULL; pe_header = (PIMAGE_NT_HEADERS32)(data + yr_le32toh(mz_header->e_lfanew)); if (yr_le32toh(pe_header->Signature) != IMAGE_NT_SIGNATURE) return NULL; if (data_size < headers_size + sizeof(IMAGE_OPTIONAL_HEADER32)) return NULL; if (pe_header->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) headers_size += sizeof(IMAGE_OPTIONAL_HEADER64); else headers_size += sizeof(IMAGE_OPTIONAL_HEADER32); if (data_size < headers_size) return NULL; if (yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_UNKNOWN && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_AM33 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_AMD64 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_ARM && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_ARMNT && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_ARM64 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_EBC && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_I386 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_IA64 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_M32R && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_MIPS16 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_MIPSFPU && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_MIPSFPU16 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_POWERPC && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_POWERPCFP && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_R4000 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_SH3 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_SH3DSP && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_SH4 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_SH5 && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_THUMB && yr_le16toh(pe_header->FileHeader.Machine) != IMAGE_FILE_MACHINE_WCEMIPSV2) { return NULL; } return pe_header; } PIMAGE_DATA_DIRECTORY pe_get_directory_entry(PE* pe, int entry) { PIMAGE_DATA_DIRECTORY directory_start; uint8_t* optional_header_start; uint16_t optional_header_size; // We are specifically NOT checking NumberOfRvaAndSizes here because it can // lie. 7ff1bf680c80fd73c0b35084904848b3705480ddeb6d0eff62180bd14cd18570 has // NumberOfRvaAndSizes set to 11 when in fact there is a valid // IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR entry (which is more than 11). If we // are overly strict here and only parse entries which are less than // NumberOfRvaAndSizes we run the risk of missing otherwise perfectly valid // files. Instead of being strict we check to make sure the entry is within // the OptionalHeader, since SizeOfOptionalHeader includes the DataDirectory // array. // In case someone requests an entry which is, by definition, invalid. if (entry >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL; if (IS_64BITS_PE(pe)) { optional_header_start = (uint8_t*) &pe->header64->OptionalHeader; optional_header_size = pe->header64->FileHeader.SizeOfOptionalHeader; directory_start = pe->header64->OptionalHeader.DataDirectory; } else { optional_header_start = (uint8_t*) &pe->header->OptionalHeader; optional_header_size = pe->header->FileHeader.SizeOfOptionalHeader; directory_start = pe->header->OptionalHeader.DataDirectory; } // Make sure the entry doesn't point outside of the OptionalHeader. if ((uint8_t*) (directory_start + entry) <= optional_header_start + optional_header_size) { if (IS_64BITS_PE(pe)) return &pe->header64->OptionalHeader.DataDirectory[entry]; else return &pe->header->OptionalHeader.DataDirectory[entry]; } return NULL; } int64_t pe_rva_to_offset(PE* pe, uint64_t rva) { PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(pe->header); DWORD lowest_section_rva = 0xffffffff; DWORD section_rva = 0; DWORD section_offset = 0; DWORD section_raw_size = 0; int64_t result; int i = 0; int alignment = 0; int rest = 0; while (i < yr_min( yr_le16toh(pe->header->FileHeader.NumberOfSections), MAX_PE_SECTIONS)) { if (struct_fits_in_pe(pe, section, IMAGE_SECTION_HEADER)) { if (lowest_section_rva > yr_le32toh(section->VirtualAddress)) { lowest_section_rva = yr_le32toh(section->VirtualAddress); } if (rva >= yr_le32toh(section->VirtualAddress) && section_rva <= yr_le32toh(section->VirtualAddress)) { // Round section_offset // // Rounding everything less than 0x200 to 0 as discussed in // https://code.google.com/archive/p/corkami/wikis/PE.wiki#PointerToRawData // does not work for PE32_FILE from the test suite and for // some tinype samples where File Alignment = 4 // (http://www.phreedom.org/research/tinype/). // // If FileAlignment is >= 0x200, it is apparently ignored (see // Ero Carreras's pefile.py, PE.adjust_FileAlignment). alignment = yr_min( yr_le32toh(OptionalHeader(pe, FileAlignment)), 0x200); section_rva = yr_le32toh(section->VirtualAddress); section_offset = yr_le32toh(section->PointerToRawData); section_raw_size = yr_le32toh(section->SizeOfRawData); if (alignment) { rest = section_offset % alignment; if (rest) section_offset -= rest; } } section++; i++; } else { return -1; } } // Everything before the first section seems to get mapped straight // relative to ImageBase. if (rva < lowest_section_rva) { section_rva = 0; section_offset = 0; section_raw_size = (DWORD) pe->data_size; } // Many sections, have a raw (on disk) size smaller than their in-memory size. // Check for rva's that map to this sparse space, and therefore have no valid // associated file offset. if ((rva - section_rva) >= section_raw_size) return -1; result = section_offset + (rva - section_rva); // Check that the offset fits within the file. if (result >= pe->data_size) return -1; return result; } #if !HAVE_TIMEGM #if HAVE__MKGMTIME #define timegm _mkgmtime #else #include static bool is_leap(unsigned int year) { year += 1900; return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); } time_t timegm(struct tm* tm) { static const unsigned ndays[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; time_t res = 0; int i; for (i = 70; i < tm->tm_year; ++i) res += is_leap(i) ? 366 : 365; for (i = 0; i < tm->tm_mon; ++i) res += ndays[is_leap(tm->tm_year)][i]; res += tm->tm_mday - 1; res *= 24; res += tm->tm_hour; res *= 60; res += tm->tm_min; res *= 60; res += tm->tm_sec; return res; } #endif // HAVE__MKGMTIME #endif // !HAVE_TIMEGM #if HAVE_LIBCRYPTO // Taken from http://stackoverflow.com/questions/10975542/asn1-time-conversion // and cleaned up. Also uses timegm(3) instead of mktime(3). time_t ASN1_get_time_t(const ASN1_TIME* time) { struct tm t; const char* str = (const char*) time->data; size_t i = 0; memset(&t, 0, sizeof(t)); if (time->type == V_ASN1_UTCTIME) /* two digit year */ { t.tm_year = (str[i++] - '0') * 10; t.tm_year += (str[i++] - '0'); if (t.tm_year < 70) t.tm_year += 100; } else if (time->type == V_ASN1_GENERALIZEDTIME) /* four digit year */ { t.tm_year = (str[i++] - '0') * 1000; t.tm_year += (str[i++] - '0') * 100; t.tm_year += (str[i++] - '0') * 10; t.tm_year += (str[i++] - '0'); t.tm_year -= 1900; } t.tm_mon = (str[i++] - '0') * 10; t.tm_mon += (str[i++] - '0') - 1; // -1 since January is 0 not 1. t.tm_mday = (str[i++] - '0') * 10; t.tm_mday += (str[i++] - '0'); t.tm_hour = (str[i++] - '0') * 10; t.tm_hour += (str[i++] - '0'); t.tm_min = (str[i++] - '0') * 10; t.tm_min += (str[i++] - '0'); t.tm_sec = (str[i++] - '0') * 10; t.tm_sec += (str[i++] - '0'); /* Note: we did not adjust the time based on time zone information */ return timegm(&t); } #endif // These ordinals are taken from pefile. If a lookup fails attempt to return // "ordN" and if that fails, return NULL. The caller is responsible for freeing // the returned string. char* ord_lookup(char* dll, uint16_t ord) { char name[64]; name[0] = '\0'; if (strncasecmp(dll, "WS2_32.dll", 10) == 0 || strncasecmp(dll, "wsock32.dll", 11) == 0) { switch (ord) { case 1: sprintf(name, "accept"); break; case 2: sprintf(name, "bind"); break; case 3: sprintf(name, "closesocket"); break; case 4: sprintf(name, "connect"); break; case 5: sprintf(name, "getpeername"); break; case 6: sprintf(name, "getsockname"); break; case 7: sprintf(name, "getsockopt"); break; case 8: sprintf(name, "htonl"); break; case 9: sprintf(name, "htons"); break; case 10: sprintf(name, "ioctlsocket"); break; case 11: sprintf(name, "inet_addr"); break; case 12: sprintf(name, "inet_ntoa"); break; case 13: sprintf(name, "listen"); break; case 14: sprintf(name, "ntohl"); break; case 15: sprintf(name, "ntohs"); break; case 16: sprintf(name, "recv"); break; case 17: sprintf(name, "recvfrom"); break; case 18: sprintf(name, "select"); break; case 19: sprintf(name, "send"); break; case 20: sprintf(name, "sendto"); break; case 21: sprintf(name, "setsockopt"); break; case 22: sprintf(name, "shutdown"); break; case 23: sprintf(name, "socket"); break; case 24: sprintf(name, "GetAddrInfoW"); break; case 25: sprintf(name, "GetNameInfoW"); break; case 26: sprintf(name, "WSApSetPostRoutine"); break; case 27: sprintf(name, "FreeAddrInfoW"); break; case 28: sprintf(name, "WPUCompleteOverlappedRequest"); break; case 29: sprintf(name, "WSAAccept"); break; case 30: sprintf(name, "WSAAddressToStringA"); break; case 31: sprintf(name, "WSAAddressToStringW"); break; case 32: sprintf(name, "WSACloseEvent"); break; case 33: sprintf(name, "WSAConnect"); break; case 34: sprintf(name, "WSACreateEvent"); break; case 35: sprintf(name, "WSADuplicateSocketA"); break; case 36: sprintf(name, "WSADuplicateSocketW"); break; case 37: sprintf(name, "WSAEnumNameSpaceProvidersA"); break; case 38: sprintf(name, "WSAEnumNameSpaceProvidersW"); break; case 39: sprintf(name, "WSAEnumNetworkEvents"); break; case 40: sprintf(name, "WSAEnumProtocolsA"); break; case 41: sprintf(name, "WSAEnumProtocolsW"); break; case 42: sprintf(name, "WSAEventSelect"); break; case 43: sprintf(name, "WSAGetOverlappedResult"); break; case 44: sprintf(name, "WSAGetQOSByName"); break; case 45: sprintf(name, "WSAGetServiceClassInfoA"); break; case 46: sprintf(name, "WSAGetServiceClassInfoW"); break; case 47: sprintf(name, "WSAGetServiceClassNameByClassIdA"); break; case 48: sprintf(name, "WSAGetServiceClassNameByClassIdW"); break; case 49: sprintf(name, "WSAHtonl"); break; case 50: sprintf(name, "WSAHtons"); break; case 51: sprintf(name, "gethostbyaddr"); break; case 52: sprintf(name, "gethostbyname"); break; case 53: sprintf(name, "getprotobyname"); break; case 54: sprintf(name, "getprotobynumber"); break; case 55: sprintf(name, "getservbyname"); break; case 56: sprintf(name, "getservbyport"); break; case 57: sprintf(name, "gethostname"); break; case 58: sprintf(name, "WSAInstallServiceClassA"); break; case 59: sprintf(name, "WSAInstallServiceClassW"); break; case 60: sprintf(name, "WSAIoctl"); break; case 61: sprintf(name, "WSAJoinLeaf"); break; case 62: sprintf(name, "WSALookupServiceBeginA"); break; case 63: sprintf(name, "WSALookupServiceBeginW"); break; case 64: sprintf(name, "WSALookupServiceEnd"); break; case 65: sprintf(name, "WSALookupServiceNextA"); break; case 66: sprintf(name, "WSALookupServiceNextW"); break; case 67: sprintf(name, "WSANSPIoctl"); break; case 68: sprintf(name, "WSANtohl"); break; case 69: sprintf(name, "WSANtohs"); break; case 70: sprintf(name, "WSAProviderConfigChange"); break; case 71: sprintf(name, "WSARecv"); break; case 72: sprintf(name, "WSARecvDisconnect"); break; case 73: sprintf(name, "WSARecvFrom"); break; case 74: sprintf(name, "WSARemoveServiceClass"); break; case 75: sprintf(name, "WSAResetEvent"); break; case 76: sprintf(name, "WSASend"); break; case 77: sprintf(name, "WSASendDisconnect"); break; case 78: sprintf(name, "WSASendTo"); break; case 79: sprintf(name, "WSASetEvent"); break; case 80: sprintf(name, "WSASetServiceA"); break; case 81: sprintf(name, "WSASetServiceW"); break; case 82: sprintf(name, "WSASocketA"); break; case 83: sprintf(name, "WSASocketW"); break; case 84: sprintf(name, "WSAStringToAddressA"); break; case 85: sprintf(name, "WSAStringToAddressW"); break; case 86: sprintf(name, "WSAWaitForMultipleEvents"); break; case 87: sprintf(name, "WSCDeinstallProvider"); break; case 88: sprintf(name, "WSCEnableNSProvider"); break; case 89: sprintf(name, "WSCEnumProtocols"); break; case 90: sprintf(name, "WSCGetProviderPath"); break; case 91: sprintf(name, "WSCInstallNameSpace"); break; case 92: sprintf(name, "WSCInstallProvider"); break; case 93: sprintf(name, "WSCUnInstallNameSpace"); break; case 94: sprintf(name, "WSCUpdateProvider"); break; case 95: sprintf(name, "WSCWriteNameSpaceOrder"); break; case 96: sprintf(name, "WSCWriteProviderOrder"); break; case 97: sprintf(name, "freeaddrinfo"); break; case 98: sprintf(name, "getaddrinfo"); break; case 99: sprintf(name, "getnameinfo"); break; case 101: sprintf(name, "WSAAsyncSelect"); break; case 102: sprintf(name, "WSAAsyncGetHostByAddr"); break; case 103: sprintf(name, "WSAAsyncGetHostByName"); break; case 104: sprintf(name, "WSAAsyncGetProtoByNumber"); break; case 105: sprintf(name, "WSAAsyncGetProtoByName"); break; case 106: sprintf(name, "WSAAsyncGetServByPort"); break; case 107: sprintf(name, "WSAAsyncGetServByName"); break; case 108: sprintf(name, "WSACancelAsyncRequest"); break; case 109: sprintf(name, "WSASetBlockingHook"); break; case 110: sprintf(name, "WSAUnhookBlockingHook"); break; case 111: sprintf(name, "WSAGetLastError"); break; case 112: sprintf(name, "WSASetLastError"); break; case 113: sprintf(name, "WSACancelBlockingCall"); break; case 114: sprintf(name, "WSAIsBlocking"); break; case 115: sprintf(name, "WSAStartup"); break; case 116: sprintf(name, "WSACleanup"); break; case 151: sprintf(name, "__WSAFDIsSet"); break; case 500: sprintf(name, "WEP"); break; default: break; } } else if (strncasecmp(dll, "oleaut32.dll", 12) == 0) { switch (ord) { case 2: sprintf(name, "SysAllocString"); break; case 3: sprintf(name, "SysReAllocString"); break; case 4: sprintf(name, "SysAllocStringLen"); break; case 5: sprintf(name, "SysReAllocStringLen"); break; case 6: sprintf(name, "SysFreeString"); break; case 7: sprintf(name, "SysStringLen"); break; case 8: sprintf(name, "VariantInit"); break; case 9: sprintf(name, "VariantClear"); break; case 10: sprintf(name, "VariantCopy"); break; case 11: sprintf(name, "VariantCopyInd"); break; case 12: sprintf(name, "VariantChangeType"); break; case 13: sprintf(name, "VariantTimeToDosDateTime"); break; case 14: sprintf(name, "DosDateTimeToVariantTime"); break; case 15: sprintf(name, "SafeArrayCreate"); break; case 16: sprintf(name, "SafeArrayDestroy"); break; case 17: sprintf(name, "SafeArrayGetDim"); break; case 18: sprintf(name, "SafeArrayGetElemsize"); break; case 19: sprintf(name, "SafeArrayGetUBound"); break; case 20: sprintf(name, "SafeArrayGetLBound"); break; case 21: sprintf(name, "SafeArrayLock"); break; case 22: sprintf(name, "SafeArrayUnlock"); break; case 23: sprintf(name, "SafeArrayAccessData"); break; case 24: sprintf(name, "SafeArrayUnaccessData"); break; case 25: sprintf(name, "SafeArrayGetElement"); break; case 26: sprintf(name, "SafeArrayPutElement"); break; case 27: sprintf(name, "SafeArrayCopy"); break; case 28: sprintf(name, "DispGetParam"); break; case 29: sprintf(name, "DispGetIDsOfNames"); break; case 30: sprintf(name, "DispInvoke"); break; case 31: sprintf(name, "CreateDispTypeInfo"); break; case 32: sprintf(name, "CreateStdDispatch"); break; case 33: sprintf(name, "RegisterActiveObject"); break; case 34: sprintf(name, "RevokeActiveObject"); break; case 35: sprintf(name, "GetActiveObject"); break; case 36: sprintf(name, "SafeArrayAllocDescriptor"); break; case 37: sprintf(name, "SafeArrayAllocData"); break; case 38: sprintf(name, "SafeArrayDestroyDescriptor"); break; case 39: sprintf(name, "SafeArrayDestroyData"); break; case 40: sprintf(name, "SafeArrayRedim"); break; case 41: sprintf(name, "SafeArrayAllocDescriptorEx"); break; case 42: sprintf(name, "SafeArrayCreateEx"); break; case 43: sprintf(name, "SafeArrayCreateVectorEx"); break; case 44: sprintf(name, "SafeArraySetRecordInfo"); break; case 45: sprintf(name, "SafeArrayGetRecordInfo"); break; case 46: sprintf(name, "VarParseNumFromStr"); break; case 47: sprintf(name, "VarNumFromParseNum"); break; case 48: sprintf(name, "VarI2FromUI1"); break; case 49: sprintf(name, "VarI2FromI4"); break; case 50: sprintf(name, "VarI2FromR4"); break; case 51: sprintf(name, "VarI2FromR8"); break; case 52: sprintf(name, "VarI2FromCy"); break; case 53: sprintf(name, "VarI2FromDate"); break; case 54: sprintf(name, "VarI2FromStr"); break; case 55: sprintf(name, "VarI2FromDisp"); break; case 56: sprintf(name, "VarI2FromBool"); break; case 57: sprintf(name, "SafeArraySetIID"); break; case 58: sprintf(name, "VarI4FromUI1"); break; case 59: sprintf(name, "VarI4FromI2"); break; case 60: sprintf(name, "VarI4FromR4"); break; case 61: sprintf(name, "VarI4FromR8"); break; case 62: sprintf(name, "VarI4FromCy"); break; case 63: sprintf(name, "VarI4FromDate"); break; case 64: sprintf(name, "VarI4FromStr"); break; case 65: sprintf(name, "VarI4FromDisp"); break; case 66: sprintf(name, "VarI4FromBool"); break; case 67: sprintf(name, "SafeArrayGetIID"); break; case 68: sprintf(name, "VarR4FromUI1"); break; case 69: sprintf(name, "VarR4FromI2"); break; case 70: sprintf(name, "VarR4FromI4"); break; case 71: sprintf(name, "VarR4FromR8"); break; case 72: sprintf(name, "VarR4FromCy"); break; case 73: sprintf(name, "VarR4FromDate"); break; case 74: sprintf(name, "VarR4FromStr"); break; case 75: sprintf(name, "VarR4FromDisp"); break; case 76: sprintf(name, "VarR4FromBool"); break; case 77: sprintf(name, "SafeArrayGetVartype"); break; case 78: sprintf(name, "VarR8FromUI1"); break; case 79: sprintf(name, "VarR8FromI2"); break; case 80: sprintf(name, "VarR8FromI4"); break; case 81: sprintf(name, "VarR8FromR4"); break; case 82: sprintf(name, "VarR8FromCy"); break; case 83: sprintf(name, "VarR8FromDate"); break; case 84: sprintf(name, "VarR8FromStr"); break; case 85: sprintf(name, "VarR8FromDisp"); break; case 86: sprintf(name, "VarR8FromBool"); break; case 87: sprintf(name, "VarFormat"); break; case 88: sprintf(name, "VarDateFromUI1"); break; case 89: sprintf(name, "VarDateFromI2"); break; case 90: sprintf(name, "VarDateFromI4"); break; case 91: sprintf(name, "VarDateFromR4"); break; case 92: sprintf(name, "VarDateFromR8"); break; case 93: sprintf(name, "VarDateFromCy"); break; case 94: sprintf(name, "VarDateFromStr"); break; case 95: sprintf(name, "VarDateFromDisp"); break; case 96: sprintf(name, "VarDateFromBool"); break; case 97: sprintf(name, "VarFormatDateTime"); break; case 98: sprintf(name, "VarCyFromUI1"); break; case 99: sprintf(name, "VarCyFromI2"); break; case 100: sprintf(name, "VarCyFromI4"); break; case 101: sprintf(name, "VarCyFromR4"); break; case 102: sprintf(name, "VarCyFromR8"); break; case 103: sprintf(name, "VarCyFromDate"); break; case 104: sprintf(name, "VarCyFromStr"); break; case 105: sprintf(name, "VarCyFromDisp"); break; case 106: sprintf(name, "VarCyFromBool"); break; case 107: sprintf(name, "VarFormatNumber"); break; case 108: sprintf(name, "VarBstrFromUI1"); break; case 109: sprintf(name, "VarBstrFromI2"); break; case 110: sprintf(name, "VarBstrFromI4"); break; case 111: sprintf(name, "VarBstrFromR4"); break; case 112: sprintf(name, "VarBstrFromR8"); break; case 113: sprintf(name, "VarBstrFromCy"); break; case 114: sprintf(name, "VarBstrFromDate"); break; case 115: sprintf(name, "VarBstrFromDisp"); break; case 116: sprintf(name, "VarBstrFromBool"); break; case 117: sprintf(name, "VarFormatPercent"); break; case 118: sprintf(name, "VarBoolFromUI1"); break; case 119: sprintf(name, "VarBoolFromI2"); break; case 120: sprintf(name, "VarBoolFromI4"); break; case 121: sprintf(name, "VarBoolFromR4"); break; case 122: sprintf(name, "VarBoolFromR8"); break; case 123: sprintf(name, "VarBoolFromDate"); break; case 124: sprintf(name, "VarBoolFromCy"); break; case 125: sprintf(name, "VarBoolFromStr"); break; case 126: sprintf(name, "VarBoolFromDisp"); break; case 127: sprintf(name, "VarFormatCurrency"); break; case 128: sprintf(name, "VarWeekdayName"); break; case 129: sprintf(name, "VarMonthName"); break; case 130: sprintf(name, "VarUI1FromI2"); break; case 131: sprintf(name, "VarUI1FromI4"); break; case 132: sprintf(name, "VarUI1FromR4"); break; case 133: sprintf(name, "VarUI1FromR8"); break; case 134: sprintf(name, "VarUI1FromCy"); break; case 135: sprintf(name, "VarUI1FromDate"); break; case 136: sprintf(name, "VarUI1FromStr"); break; case 137: sprintf(name, "VarUI1FromDisp"); break; case 138: sprintf(name, "VarUI1FromBool"); break; case 139: sprintf(name, "VarFormatFromTokens"); break; case 140: sprintf(name, "VarTokenizeFormatString"); break; case 141: sprintf(name, "VarAdd"); break; case 142: sprintf(name, "VarAnd"); break; case 143: sprintf(name, "VarDiv"); break; case 144: sprintf(name, "DllCanUnloadNow"); break; case 145: sprintf(name, "DllGetClassObject"); break; case 146: sprintf(name, "DispCallFunc"); break; case 147: sprintf(name, "VariantChangeTypeEx"); break; case 148: sprintf(name, "SafeArrayPtrOfIndex"); break; case 149: sprintf(name, "SysStringByteLen"); break; case 150: sprintf(name, "SysAllocStringByteLen"); break; case 151: sprintf(name, "DllRegisterServer"); break; case 152: sprintf(name, "VarEqv"); break; case 153: sprintf(name, "VarIdiv"); break; case 154: sprintf(name, "VarImp"); break; case 155: sprintf(name, "VarMod"); break; case 156: sprintf(name, "VarMul"); break; case 157: sprintf(name, "VarOr"); break; case 158: sprintf(name, "VarPow"); break; case 159: sprintf(name, "VarSub"); break; case 160: sprintf(name, "CreateTypeLib"); break; case 161: sprintf(name, "LoadTypeLib"); break; case 162: sprintf(name, "LoadRegTypeLib"); break; case 163: sprintf(name, "RegisterTypeLib"); break; case 164: sprintf(name, "QueryPathOfRegTypeLib"); break; case 165: sprintf(name, "LHashValOfNameSys"); break; case 166: sprintf(name, "LHashValOfNameSysA"); break; case 167: sprintf(name, "VarXor"); break; case 168: sprintf(name, "VarAbs"); break; case 169: sprintf(name, "VarFix"); break; case 170: sprintf(name, "OaBuildVersion"); break; case 171: sprintf(name, "ClearCustData"); break; case 172: sprintf(name, "VarInt"); break; case 173: sprintf(name, "VarNeg"); break; case 174: sprintf(name, "VarNot"); break; case 175: sprintf(name, "VarRound"); break; case 176: sprintf(name, "VarCmp"); break; case 177: sprintf(name, "VarDecAdd"); break; case 178: sprintf(name, "VarDecDiv"); break; case 179: sprintf(name, "VarDecMul"); break; case 180: sprintf(name, "CreateTypeLib2"); break; case 181: sprintf(name, "VarDecSub"); break; case 182: sprintf(name, "VarDecAbs"); break; case 183: sprintf(name, "LoadTypeLibEx"); break; case 184: sprintf(name, "SystemTimeToVariantTime"); break; case 185: sprintf(name, "VariantTimeToSystemTime"); break; case 186: sprintf(name, "UnRegisterTypeLib"); break; case 187: sprintf(name, "VarDecFix"); break; case 188: sprintf(name, "VarDecInt"); break; case 189: sprintf(name, "VarDecNeg"); break; case 190: sprintf(name, "VarDecFromUI1"); break; case 191: sprintf(name, "VarDecFromI2"); break; case 192: sprintf(name, "VarDecFromI4"); break; case 193: sprintf(name, "VarDecFromR4"); break; case 194: sprintf(name, "VarDecFromR8"); break; case 195: sprintf(name, "VarDecFromDate"); break; case 196: sprintf(name, "VarDecFromCy"); break; case 197: sprintf(name, "VarDecFromStr"); break; case 198: sprintf(name, "VarDecFromDisp"); break; case 199: sprintf(name, "VarDecFromBool"); break; case 200: sprintf(name, "GetErrorInfo"); break; case 201: sprintf(name, "SetErrorInfo"); break; case 202: sprintf(name, "CreateErrorInfo"); break; case 203: sprintf(name, "VarDecRound"); break; case 204: sprintf(name, "VarDecCmp"); break; case 205: sprintf(name, "VarI2FromI1"); break; case 206: sprintf(name, "VarI2FromUI2"); break; case 207: sprintf(name, "VarI2FromUI4"); break; case 208: sprintf(name, "VarI2FromDec"); break; case 209: sprintf(name, "VarI4FromI1"); break; case 210: sprintf(name, "VarI4FromUI2"); break; case 211: sprintf(name, "VarI4FromUI4"); break; case 212: sprintf(name, "VarI4FromDec"); break; case 213: sprintf(name, "VarR4FromI1"); break; case 214: sprintf(name, "VarR4FromUI2"); break; case 215: sprintf(name, "VarR4FromUI4"); break; case 216: sprintf(name, "VarR4FromDec"); break; case 217: sprintf(name, "VarR8FromI1"); break; case 218: sprintf(name, "VarR8FromUI2"); break; case 219: sprintf(name, "VarR8FromUI4"); break; case 220: sprintf(name, "VarR8FromDec"); break; case 221: sprintf(name, "VarDateFromI1"); break; case 222: sprintf(name, "VarDateFromUI2"); break; case 223: sprintf(name, "VarDateFromUI4"); break; case 224: sprintf(name, "VarDateFromDec"); break; case 225: sprintf(name, "VarCyFromI1"); break; case 226: sprintf(name, "VarCyFromUI2"); break; case 227: sprintf(name, "VarCyFromUI4"); break; case 228: sprintf(name, "VarCyFromDec"); break; case 229: sprintf(name, "VarBstrFromI1"); break; case 230: sprintf(name, "VarBstrFromUI2"); break; case 231: sprintf(name, "VarBstrFromUI4"); break; case 232: sprintf(name, "VarBstrFromDec"); break; case 233: sprintf(name, "VarBoolFromI1"); break; case 234: sprintf(name, "VarBoolFromUI2"); break; case 235: sprintf(name, "VarBoolFromUI4"); break; case 236: sprintf(name, "VarBoolFromDec"); break; case 237: sprintf(name, "VarUI1FromI1"); break; case 238: sprintf(name, "VarUI1FromUI2"); break; case 239: sprintf(name, "VarUI1FromUI4"); break; case 240: sprintf(name, "VarUI1FromDec"); break; case 241: sprintf(name, "VarDecFromI1"); break; case 242: sprintf(name, "VarDecFromUI2"); break; case 243: sprintf(name, "VarDecFromUI4"); break; case 244: sprintf(name, "VarI1FromUI1"); break; case 245: sprintf(name, "VarI1FromI2"); break; case 246: sprintf(name, "VarI1FromI4"); break; case 247: sprintf(name, "VarI1FromR4"); break; case 248: sprintf(name, "VarI1FromR8"); break; case 249: sprintf(name, "VarI1FromDate"); break; case 250: sprintf(name, "VarI1FromCy"); break; case 251: sprintf(name, "VarI1FromStr"); break; case 252: sprintf(name, "VarI1FromDisp"); break; case 253: sprintf(name, "VarI1FromBool"); break; case 254: sprintf(name, "VarI1FromUI2"); break; case 255: sprintf(name, "VarI1FromUI4"); break; case 256: sprintf(name, "VarI1FromDec"); break; case 257: sprintf(name, "VarUI2FromUI1"); break; case 258: sprintf(name, "VarUI2FromI2"); break; case 259: sprintf(name, "VarUI2FromI4"); break; case 260: sprintf(name, "VarUI2FromR4"); break; case 261: sprintf(name, "VarUI2FromR8"); break; case 262: sprintf(name, "VarUI2FromDate"); break; case 263: sprintf(name, "VarUI2FromCy"); break; case 264: sprintf(name, "VarUI2FromStr"); break; case 265: sprintf(name, "VarUI2FromDisp"); break; case 266: sprintf(name, "VarUI2FromBool"); break; case 267: sprintf(name, "VarUI2FromI1"); break; case 268: sprintf(name, "VarUI2FromUI4"); break; case 269: sprintf(name, "VarUI2FromDec"); break; case 270: sprintf(name, "VarUI4FromUI1"); break; case 271: sprintf(name, "VarUI4FromI2"); break; case 272: sprintf(name, "VarUI4FromI4"); break; case 273: sprintf(name, "VarUI4FromR4"); break; case 274: sprintf(name, "VarUI4FromR8"); break; case 275: sprintf(name, "VarUI4FromDate"); break; case 276: sprintf(name, "VarUI4FromCy"); break; case 277: sprintf(name, "VarUI4FromStr"); break; case 278: sprintf(name, "VarUI4FromDisp"); break; case 279: sprintf(name, "VarUI4FromBool"); break; case 280: sprintf(name, "VarUI4FromI1"); break; case 281: sprintf(name, "VarUI4FromUI2"); break; case 282: sprintf(name, "VarUI4FromDec"); break; case 283: sprintf(name, "BSTR_UserSize"); break; case 284: sprintf(name, "BSTR_UserMarshal"); break; case 285: sprintf(name, "BSTR_UserUnmarshal"); break; case 286: sprintf(name, "BSTR_UserFree"); break; case 287: sprintf(name, "VARIANT_UserSize"); break; case 288: sprintf(name, "VARIANT_UserMarshal"); break; case 289: sprintf(name, "VARIANT_UserUnmarshal"); break; case 290: sprintf(name, "VARIANT_UserFree"); break; case 291: sprintf(name, "LPSAFEARRAY_UserSize"); break; case 292: sprintf(name, "LPSAFEARRAY_UserMarshal"); break; case 293: sprintf(name, "LPSAFEARRAY_UserUnmarshal"); break; case 294: sprintf(name, "LPSAFEARRAY_UserFree"); break; case 295: sprintf(name, "LPSAFEARRAY_Size"); break; case 296: sprintf(name, "LPSAFEARRAY_Marshal"); break; case 297: sprintf(name, "LPSAFEARRAY_Unmarshal"); break; case 298: sprintf(name, "VarDecCmpR8"); break; case 299: sprintf(name, "VarCyAdd"); break; case 300: sprintf(name, "DllUnregisterServer"); break; case 301: sprintf(name, "OACreateTypeLib2"); break; case 303: sprintf(name, "VarCyMul"); break; case 304: sprintf(name, "VarCyMulI4"); break; case 305: sprintf(name, "VarCySub"); break; case 306: sprintf(name, "VarCyAbs"); break; case 307: sprintf(name, "VarCyFix"); break; case 308: sprintf(name, "VarCyInt"); break; case 309: sprintf(name, "VarCyNeg"); break; case 310: sprintf(name, "VarCyRound"); break; case 311: sprintf(name, "VarCyCmp"); break; case 312: sprintf(name, "VarCyCmpR8"); break; case 313: sprintf(name, "VarBstrCat"); break; case 314: sprintf(name, "VarBstrCmp"); break; case 315: sprintf(name, "VarR8Pow"); break; case 316: sprintf(name, "VarR4CmpR8"); break; case 317: sprintf(name, "VarR8Round"); break; case 318: sprintf(name, "VarCat"); break; case 319: sprintf(name, "VarDateFromUdateEx"); break; case 322: sprintf(name, "GetRecordInfoFromGuids"); break; case 323: sprintf(name, "GetRecordInfoFromTypeInfo"); break; case 325: sprintf(name, "SetVarConversionLocaleSetting"); break; case 326: sprintf(name, "GetVarConversionLocaleSetting"); break; case 327: sprintf(name, "SetOaNoCache"); break; case 329: sprintf(name, "VarCyMulI8"); break; case 330: sprintf(name, "VarDateFromUdate"); break; case 331: sprintf(name, "VarUdateFromDate"); break; case 332: sprintf(name, "GetAltMonthNames"); break; case 333: sprintf(name, "VarI8FromUI1"); break; case 334: sprintf(name, "VarI8FromI2"); break; case 335: sprintf(name, "VarI8FromR4"); break; case 336: sprintf(name, "VarI8FromR8"); break; case 337: sprintf(name, "VarI8FromCy"); break; case 338: sprintf(name, "VarI8FromDate"); break; case 339: sprintf(name, "VarI8FromStr"); break; case 340: sprintf(name, "VarI8FromDisp"); break; case 341: sprintf(name, "VarI8FromBool"); break; case 342: sprintf(name, "VarI8FromI1"); break; case 343: sprintf(name, "VarI8FromUI2"); break; case 344: sprintf(name, "VarI8FromUI4"); break; case 345: sprintf(name, "VarI8FromDec"); break; case 346: sprintf(name, "VarI2FromI8"); break; case 347: sprintf(name, "VarI2FromUI8"); break; case 348: sprintf(name, "VarI4FromI8"); break; case 349: sprintf(name, "VarI4FromUI8"); break; case 360: sprintf(name, "VarR4FromI8"); break; case 361: sprintf(name, "VarR4FromUI8"); break; case 362: sprintf(name, "VarR8FromI8"); break; case 363: sprintf(name, "VarR8FromUI8"); break; case 364: sprintf(name, "VarDateFromI8"); break; case 365: sprintf(name, "VarDateFromUI8"); break; case 366: sprintf(name, "VarCyFromI8"); break; case 367: sprintf(name, "VarCyFromUI8"); break; case 368: sprintf(name, "VarBstrFromI8"); break; case 369: sprintf(name, "VarBstrFromUI8"); break; case 370: sprintf(name, "VarBoolFromI8"); break; case 371: sprintf(name, "VarBoolFromUI8"); break; case 372: sprintf(name, "VarUI1FromI8"); break; case 373: sprintf(name, "VarUI1FromUI8"); break; case 374: sprintf(name, "VarDecFromI8"); break; case 375: sprintf(name, "VarDecFromUI8"); break; case 376: sprintf(name, "VarI1FromI8"); break; case 377: sprintf(name, "VarI1FromUI8"); break; case 378: sprintf(name, "VarUI2FromI8"); break; case 379: sprintf(name, "VarUI2FromUI8"); break; case 401: sprintf(name, "OleLoadPictureEx"); break; case 402: sprintf(name, "OleLoadPictureFileEx"); break; case 411: sprintf(name, "SafeArrayCreateVector"); break; case 412: sprintf(name, "SafeArrayCopyData"); break; case 413: sprintf(name, "VectorFromBstr"); break; case 414: sprintf(name, "BstrFromVector"); break; case 415: sprintf(name, "OleIconToCursor"); break; case 416: sprintf(name, "OleCreatePropertyFrameIndirect"); break; case 417: sprintf(name, "OleCreatePropertyFrame"); break; case 418: sprintf(name, "OleLoadPicture"); break; case 419: sprintf(name, "OleCreatePictureIndirect"); break; case 420: sprintf(name, "OleCreateFontIndirect"); break; case 421: sprintf(name, "OleTranslateColor"); break; case 422: sprintf(name, "OleLoadPictureFile"); break; case 423: sprintf(name, "OleSavePictureFile"); break; case 424: sprintf(name, "OleLoadPicturePath"); break; case 425: sprintf(name, "VarUI4FromI8"); break; case 426: sprintf(name, "VarUI4FromUI8"); break; case 427: sprintf(name, "VarI8FromUI8"); break; case 428: sprintf(name, "VarUI8FromI8"); break; case 429: sprintf(name, "VarUI8FromUI1"); break; case 430: sprintf(name, "VarUI8FromI2"); break; case 431: sprintf(name, "VarUI8FromR4"); break; case 432: sprintf(name, "VarUI8FromR8"); break; case 433: sprintf(name, "VarUI8FromCy"); break; case 434: sprintf(name, "VarUI8FromDate"); break; case 435: sprintf(name, "VarUI8FromStr"); break; case 436: sprintf(name, "VarUI8FromDisp"); break; case 437: sprintf(name, "VarUI8FromBool"); break; case 438: sprintf(name, "VarUI8FromI1"); break; case 439: sprintf(name, "VarUI8FromUI2"); break; case 440: sprintf(name, "VarUI8FromUI4"); break; case 441: sprintf(name, "VarUI8FromDec"); break; case 442: sprintf(name, "RegisterTypeLibForUser"); break; case 443: sprintf(name, "UnRegisterTypeLibForUser"); break; default: break; } } if (name[0] == '\0') sprintf(name, "ord%u", ord); return yr_strdup(name); } yara-4.1.3/libyara/modules/tests/000077500000000000000000000000001413423160300167055ustar00rootroot00000000000000yara-4.1.3/libyara/modules/tests/tests.c000066400000000000000000000127341413423160300202220ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #define MODULE_NAME tests define_function(fsum_2) { double a = float_argument(1); double b = float_argument(2); return_float(a + b); } define_function(fsum_3) { double a = float_argument(1); double b = float_argument(2); double c = float_argument(3); return_float(a + b + c); } define_function(isum_2) { int64_t a = integer_argument(1); int64_t b = integer_argument(2); return_integer(a + b); } define_function(isum_3) { int64_t a = integer_argument(1); int64_t b = integer_argument(2); int64_t c = integer_argument(3); return_integer(a + b + c); } define_function(length) { char* s = string_argument(1); return_integer(strlen(s)); } define_function(empty) { return_string(""); } define_function(match) { return_integer( yr_re_match(scan_context(), regexp_argument(1), string_argument(2))); } define_function(foobar) { int64_t arg = integer_argument(1); switch (arg) { case 1: return_string("foo"); break; case 2: return_string("bar"); break; } return_string("oops") } begin_declarations begin_struct("constants") declare_integer("one"); declare_integer("two"); declare_string("foo"); declare_string("empty"); end_struct("constants") begin_struct("undefined") declare_integer("i"); declare_float("f"); end_struct("undefined") declare_string("module_data"); declare_integer_array("integer_array"); declare_string_array("string_array"); declare_integer_dictionary("integer_dict"); declare_string_dictionary("string_dict"); begin_struct_array("struct_array") declare_integer("i"); declare_string("s"); end_struct_array("struct_array") begin_struct_dictionary("struct_dict") declare_integer("i"); declare_string("s"); end_struct_dictionary("struct_dict"); begin_struct_dictionary("empty_struct_dict") declare_integer("unused"); end_struct_dictionary("empty_struct_dict") declare_function("match", "rs", "i", match); declare_function("isum", "ii", "i", isum_2); declare_function("isum", "iii", "i", isum_3); declare_function("fsum", "ff", "f", fsum_2); declare_function("fsum", "fff", "f", fsum_3); declare_function("length", "s", "i", length); declare_function("empty", "", "s", empty); declare_function("foobar", "i", "s", foobar); end_declarations int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { set_integer(1, module_object, "constants.one"); set_integer(2, module_object, "constants.two"); set_string("foo", module_object, "constants.foo"); set_string("", module_object, "constants.empty"); set_integer(1, module_object, "struct_array[1].i"); set_integer(0, module_object, "integer_array[%i]", 0); set_integer(1, module_object, "integer_array[%i]", 1); set_integer(2, module_object, "integer_array[%i]", 2); set_integer(256, module_object, "integer_array[%i]", 256); set_string("foo", module_object, "string_array[%i]", 0); set_string("bar", module_object, "string_array[%i]", 1); set_string("baz", module_object, "string_array[%i]", 2); set_sized_string("foo\0bar", 7, module_object, "string_array[%i]", 3); set_string("foo", module_object, "string_dict[%s]", "foo"); set_string("bar", module_object, "string_dict[\"bar\"]"); set_string("foo", module_object, "struct_dict[%s].s", "foo"); set_integer(1, module_object, "struct_dict[%s].i", "foo"); if (module_data_size > 0 && module_data != NULL) { set_sized_string( (const char*) module_data, module_data_size, module_object, "module_data"); } return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { // Fail if module_unload is called twice with the same module_object if (module_object->data == (void*) 0xFABADA) assert(false); module_object->data = (void*) 0xFABADA; return ERROR_SUCCESS; } yara-4.1.3/libyara/modules/time/000077500000000000000000000000001413423160300165015ustar00rootroot00000000000000yara-4.1.3/libyara/modules/time/time.c000066400000000000000000000041561413423160300176110ustar00rootroot00000000000000/* Copyright (c) 2014-2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #define MODULE_NAME time define_function(now) { time_t now = time(NULL); if (now == -1) return_integer(YR_UNDEFINED); return_integer((long) now); } begin_declarations declare_function("now", "", "i", now); end_declarations; int module_initialize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_finalize(YR_MODULE* module) { return ERROR_SUCCESS; } int module_load( YR_SCAN_CONTEXT* context, YR_OBJECT* module_object, void* module_data, size_t module_data_size) { return ERROR_SUCCESS; } int module_unload(YR_OBJECT* module_object) { return ERROR_SUCCESS; } yara-4.1.3/libyara/notebook.c000066400000000000000000000131131413423160300160560ustar00rootroot00000000000000/* Copyright (c) 2020. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include // Forward declaration of YR_NOTEBOOK_PAGE. typedef struct YR_NOTEBOOK_PAGE YR_NOTEBOOK_PAGE; // A notebook is a data structure that can be used for allocating memory // space in the same way malloc() would do. However, the buffers returned // by yr_notebook_alloc() are backed by a larger buffer reserved by the notebook // beforehand called a "page". The notebook fulfills the allocations performed // via yr_notebook_alloc() with space taken from the current page, or creates // a new page when necessary. It's recommended that the page size is at least // 4x the size of the buffers you plan to allocate with yr_notebook_alloc(). // // Once the notebook is destroyed all the pages are freed, and consequently // all the buffers allocated via yr_notebook_alloc(). struct YR_NOTEBOOK { // Size of each page in the notebook. size_t page_size; // Pointer to the first page in the book, this is also the most recently // created page, the one that is being filled. YR_NOTEBOOK_PAGE* page_list_head; }; struct YR_NOTEBOOK_PAGE { // Amount of bytes in the page that are actually used. size_t used; // Pointer to next page. YR_NOTEBOOK_PAGE* next; // Page's data. uint8_t data[0]; }; //////////////////////////////////////////////////////////////////////////////// // Creates a new notebook. The notebook initially has a single page of the // specified size, but more pages are created if needed. // // Args: // page_size: Size of each page in the notebook. // notebook: Address of a pointer to the newly created notebook. // // Returns: // ERROR_SUCCESS // ERROR_INSUFFICIENT_MEMORY // int yr_notebook_create(size_t page_size, YR_NOTEBOOK** notebook) { YR_NOTEBOOK* new_notebook = yr_malloc(sizeof(YR_NOTEBOOK)); if (new_notebook == NULL) return ERROR_INSUFFICIENT_MEMORY; new_notebook->page_list_head = yr_malloc( sizeof(YR_NOTEBOOK_PAGE) + page_size); if (new_notebook->page_list_head == NULL) { yr_free(new_notebook); return ERROR_INSUFFICIENT_MEMORY; } new_notebook->page_size = page_size; new_notebook->page_list_head->used = 0; new_notebook->page_list_head->next = NULL; *notebook = new_notebook; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Destroys a notebook and frees all the notebook's pages. // // Args: // notebook: Pointer to the notebook being destroyed. // // Returns: // ERROR_SUCCESS // int yr_notebook_destroy(YR_NOTEBOOK* notebook) { YR_NOTEBOOK_PAGE* page = notebook->page_list_head; while (page != NULL) { YR_NOTEBOOK_PAGE* next = page->next; yr_free(page); page = next; } yr_free(notebook); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Allocates a memory buffer from a notebook. The memory is freed when the // notebook is destroyed, allocated buffers can't be freed individually. // // Args: // notebook: Pointer to the notebook. // size: Size of the allocated memory. // // Returns: // Pointer to the allocated memory, or NULL if the allocation fails. // void* yr_notebook_alloc(YR_NOTEBOOK* notebook, size_t size) { // The requested memory size can't be larger than a notebook's page. assert(size <= notebook->page_size); // If the requested size doesn't fit in current page's free space, allocate // a new page. if (notebook->page_size - notebook->page_list_head->used < size) { YR_NOTEBOOK_PAGE* new_page = yr_malloc( sizeof(YR_NOTEBOOK_PAGE) + notebook->page_size); if (new_page == NULL) return NULL; new_page->used = 0; new_page->next = notebook->page_list_head; notebook->page_list_head = new_page; } void* ptr = notebook->page_list_head->data + notebook->page_list_head->used; // In ARM make sure the alignment of the returned buffer is 4 bytes. #if defined(__arm__) uintptr_t misalignment = (uintptr_t) ptr & 3; if (misalignment) { size += 4 - misalignment; ptr += 4 - misalignment; } #endif notebook->page_list_head->used += size; return ptr; } yara-4.1.3/libyara/object.c000066400000000000000000000615571413423160300155230ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include int yr_object_create( int8_t type, const char* identifier, YR_OBJECT* parent, YR_OBJECT** object) { YR_OBJECT* obj; int i; size_t object_size = 0; assert(parent != NULL || object != NULL); switch (type) { case OBJECT_TYPE_STRUCTURE: object_size = sizeof(YR_OBJECT_STRUCTURE); break; case OBJECT_TYPE_ARRAY: object_size = sizeof(YR_OBJECT_ARRAY); break; case OBJECT_TYPE_DICTIONARY: object_size = sizeof(YR_OBJECT_DICTIONARY); break; case OBJECT_TYPE_INTEGER: object_size = sizeof(YR_OBJECT); break; case OBJECT_TYPE_FLOAT: object_size = sizeof(YR_OBJECT); break; case OBJECT_TYPE_STRING: object_size = sizeof(YR_OBJECT); break; case OBJECT_TYPE_FUNCTION: object_size = sizeof(YR_OBJECT_FUNCTION); break; default: assert(false); } obj = (YR_OBJECT*) yr_malloc(object_size); if (obj == NULL) return ERROR_INSUFFICIENT_MEMORY; obj->type = type; obj->identifier = yr_strdup(identifier); obj->parent = parent; obj->data = NULL; switch (type) { case OBJECT_TYPE_INTEGER: obj->value.i = YR_UNDEFINED; break; case OBJECT_TYPE_FLOAT: obj->value.d = NAN; break; case OBJECT_TYPE_STRING: obj->value.ss = NULL; break; case OBJECT_TYPE_STRUCTURE: object_as_structure(obj)->members = NULL; break; case OBJECT_TYPE_ARRAY: object_as_array(obj)->items = NULL; object_as_array(obj)->prototype_item = NULL; break; case OBJECT_TYPE_DICTIONARY: object_as_dictionary(obj)->items = NULL; object_as_dictionary(obj)->prototype_item = NULL; break; case OBJECT_TYPE_FUNCTION: object_as_function(obj)->return_obj = NULL; for (i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++) { object_as_function(obj)->prototypes[i].arguments_fmt = NULL; object_as_function(obj)->prototypes[i].code = NULL; } break; } if (obj->identifier == NULL) { yr_free(obj); return ERROR_INSUFFICIENT_MEMORY; } if (parent != NULL) { assert( parent->type == OBJECT_TYPE_STRUCTURE || parent->type == OBJECT_TYPE_ARRAY || parent->type == OBJECT_TYPE_DICTIONARY || parent->type == OBJECT_TYPE_FUNCTION); // Objects with a parent take the canary from it. obj->canary = parent->canary; switch (parent->type) { case OBJECT_TYPE_STRUCTURE: FAIL_ON_ERROR_WITH_CLEANUP(yr_object_structure_set_member(parent, obj), { yr_free((void*) obj->identifier); yr_free(obj); }); break; case OBJECT_TYPE_ARRAY: object_as_array(parent)->prototype_item = obj; break; case OBJECT_TYPE_DICTIONARY: object_as_dictionary(parent)->prototype_item = obj; break; case OBJECT_TYPE_FUNCTION: object_as_function(parent)->return_obj = obj; break; } } if (object != NULL) *object = obj; return ERROR_SUCCESS; } void yr_object_set_canary(YR_OBJECT* object, int canary) { object->canary = canary; } int yr_object_function_create( const char* identifier, const char* arguments_fmt, const char* return_fmt, YR_MODULE_FUNC code, YR_OBJECT* parent, YR_OBJECT** function) { YR_OBJECT* return_obj; YR_OBJECT* o = NULL; YR_OBJECT_FUNCTION* f = NULL; int8_t return_type; int i; // The parent of a function must be a structure. assert(parent != NULL && parent->type == OBJECT_TYPE_STRUCTURE); switch (*return_fmt) { case 'i': return_type = OBJECT_TYPE_INTEGER; break; case 's': return_type = OBJECT_TYPE_STRING; break; case 'f': return_type = OBJECT_TYPE_FLOAT; break; default: return ERROR_INVALID_FORMAT; } // Try to find if the structure already has a function // with that name. In that case this is a function overload. f = object_as_function(yr_object_lookup_field(parent, identifier)); // Overloaded functions must have the same return type. if (f != NULL && return_type != f->return_obj->type) return ERROR_WRONG_RETURN_TYPE; if (f == NULL) // Function doesn't exist yet { FAIL_ON_ERROR( yr_object_create(OBJECT_TYPE_FUNCTION, identifier, parent, &o)); FAIL_ON_ERROR_WITH_CLEANUP( yr_object_create(return_type, "result", o, &return_obj), yr_object_destroy(o)); f = object_as_function(o); } for (i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++) { if (f->prototypes[i].arguments_fmt == NULL) { f->prototypes[i].arguments_fmt = arguments_fmt; f->prototypes[i].code = code; break; } } if (function != NULL) *function = (YR_OBJECT*) f; return ERROR_SUCCESS; } int yr_object_from_external_variable( YR_EXTERNAL_VARIABLE* external, YR_OBJECT** object) { YR_OBJECT* obj; int result; uint8_t obj_type = 0; switch (external->type) { case EXTERNAL_VARIABLE_TYPE_INTEGER: case EXTERNAL_VARIABLE_TYPE_BOOLEAN: obj_type = OBJECT_TYPE_INTEGER; break; case EXTERNAL_VARIABLE_TYPE_FLOAT: obj_type = OBJECT_TYPE_FLOAT; break; case EXTERNAL_VARIABLE_TYPE_STRING: case EXTERNAL_VARIABLE_TYPE_MALLOC_STRING: obj_type = OBJECT_TYPE_STRING; break; default: assert(false); } result = yr_object_create(obj_type, external->identifier, NULL, &obj); if (result == ERROR_SUCCESS) { switch (external->type) { case EXTERNAL_VARIABLE_TYPE_INTEGER: case EXTERNAL_VARIABLE_TYPE_BOOLEAN: result = yr_object_set_integer(external->value.i, obj, NULL); break; case EXTERNAL_VARIABLE_TYPE_FLOAT: result = yr_object_set_float(external->value.f, obj, NULL); break; case EXTERNAL_VARIABLE_TYPE_STRING: case EXTERNAL_VARIABLE_TYPE_MALLOC_STRING: result = yr_object_set_string( external->value.s, strlen(external->value.s), obj, NULL); break; } if (result == ERROR_SUCCESS) { *object = obj; } else { yr_object_destroy(obj); } } return result; } void yr_object_destroy(YR_OBJECT* object) { YR_STRUCTURE_MEMBER* member; YR_STRUCTURE_MEMBER* next_member; YR_ARRAY_ITEMS* array_items; YR_DICTIONARY_ITEMS* dict_items; int i; if (object == NULL) return; switch (object->type) { case OBJECT_TYPE_STRUCTURE: member = object_as_structure(object)->members; while (member != NULL) { next_member = member->next; yr_object_destroy(member->object); yr_free(member); member = next_member; } break; case OBJECT_TYPE_STRING: if (object->value.ss != NULL) yr_free(object->value.ss); break; case OBJECT_TYPE_ARRAY: if (object_as_array(object)->prototype_item != NULL) yr_object_destroy(object_as_array(object)->prototype_item); array_items = object_as_array(object)->items; if (array_items != NULL) { for (i = 0; i < array_items->length; i++) if (array_items->objects[i] != NULL) yr_object_destroy(array_items->objects[i]); } yr_free(array_items); break; case OBJECT_TYPE_DICTIONARY: if (object_as_dictionary(object)->prototype_item != NULL) yr_object_destroy(object_as_dictionary(object)->prototype_item); dict_items = object_as_dictionary(object)->items; if (dict_items != NULL) { for (i = 0; i < dict_items->used; i++) { if (dict_items->objects[i].key != NULL) yr_free(dict_items->objects[i].key); if (dict_items->objects[i].obj != NULL) yr_object_destroy(dict_items->objects[i].obj); } } yr_free(dict_items); break; case OBJECT_TYPE_FUNCTION: yr_object_destroy(object_as_function(object)->return_obj); break; } yr_free((void*) object->identifier); yr_free(object); } YR_OBJECT* yr_object_lookup_field(YR_OBJECT* object, const char* field_name) { YR_STRUCTURE_MEMBER* member; assert(object != NULL); assert(object->type == OBJECT_TYPE_STRUCTURE); member = object_as_structure(object)->members; while (member != NULL) { if (strcmp(member->object->identifier, field_name) == 0) return member->object; member = member->next; } return NULL; } static YR_OBJECT* _yr_object_lookup( YR_OBJECT* object, int flags, const char* pattern, va_list args) { YR_OBJECT* obj = object; const char* p = pattern; const char* key = NULL; char str[256]; int i; int index = -1; while (obj != NULL) { i = 0; while (*p != '\0' && *p != '.' && *p != '[' && i < sizeof(str) - 1) { str[i++] = *p++; } str[i] = '\0'; if (obj->type != OBJECT_TYPE_STRUCTURE) return NULL; obj = yr_object_lookup_field(obj, str); if (obj == NULL) return NULL; if (*p == '[') { p++; if (*p == '%') { p++; switch (*p++) { case 'i': index = va_arg(args, int); break; case 's': key = va_arg(args, const char*); break; default: return NULL; } } else if (*p >= '0' && *p <= '9') { index = (int) strtol(p, (char**) &p, 10); } else if (*p == '"') { i = 0; p++; // skip the opening quotation mark while (*p != '"' && *p != '\0' && i < sizeof(str) - 1) str[i++] = *p++; str[i] = '\0'; p++; // skip the closing quotation mark key = str; } else { return NULL; } assert(*p == ']'); p++; assert(*p == '.' || *p == '\0'); switch (obj->type) { case OBJECT_TYPE_ARRAY: assert(index != -1); obj = yr_object_array_get_item(obj, flags, index); break; case OBJECT_TYPE_DICTIONARY: assert(key != NULL); obj = yr_object_dict_get_item(obj, flags, key); break; } } if (*p == '\0') break; p++; } return obj; } YR_OBJECT* yr_object_lookup( YR_OBJECT* object, int flags, const char* pattern, ...) { YR_OBJECT* result; va_list args; va_start(args, pattern); result = _yr_object_lookup(object, flags, pattern, args); va_end(args); return result; } int yr_object_copy(YR_OBJECT* object, YR_OBJECT** object_copy) { YR_OBJECT* copy; YR_OBJECT* o; YR_STRUCTURE_MEMBER* structure_member; int i; *object_copy = NULL; FAIL_ON_ERROR( yr_object_create(object->type, object->identifier, NULL, ©)); copy->canary = object->canary; switch (object->type) { case OBJECT_TYPE_INTEGER: copy->value.i = object->value.i; break; case OBJECT_TYPE_FLOAT: copy->value.d = object->value.d; break; case OBJECT_TYPE_STRING: if (object->value.ss != NULL) copy->value.ss = ss_dup(object->value.ss); else copy->value.ss = NULL; break; case OBJECT_TYPE_FUNCTION: FAIL_ON_ERROR_WITH_CLEANUP( yr_object_copy( object_as_function(object)->return_obj, &object_as_function(copy)->return_obj), // cleanup yr_object_destroy(copy)); for (i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++) object_as_function(copy)->prototypes[i] = object_as_function(object)->prototypes[i]; break; case OBJECT_TYPE_STRUCTURE: structure_member = object_as_structure(object)->members; while (structure_member != NULL) { FAIL_ON_ERROR_WITH_CLEANUP( yr_object_copy(structure_member->object, &o), yr_object_destroy(copy)); FAIL_ON_ERROR_WITH_CLEANUP(yr_object_structure_set_member(copy, o), // cleanup yr_free(o); yr_object_destroy(copy)); structure_member = structure_member->next; } break; case OBJECT_TYPE_ARRAY: FAIL_ON_ERROR_WITH_CLEANUP( yr_object_copy(object_as_array(object)->prototype_item, &o), yr_object_destroy(copy)); object_as_array(copy)->prototype_item = o; break; case OBJECT_TYPE_DICTIONARY: FAIL_ON_ERROR_WITH_CLEANUP( yr_object_copy(object_as_dictionary(object)->prototype_item, &o), yr_object_destroy(copy)); object_as_dictionary(copy)->prototype_item = o; break; default: assert(false); } *object_copy = copy; return ERROR_SUCCESS; } int yr_object_structure_set_member(YR_OBJECT* object, YR_OBJECT* member) { YR_STRUCTURE_MEMBER* sm; assert(object->type == OBJECT_TYPE_STRUCTURE); // Check if the object already have a member with the same identifier if (yr_object_lookup_field(object, member->identifier) != NULL) return ERROR_DUPLICATED_STRUCTURE_MEMBER; sm = (YR_STRUCTURE_MEMBER*) yr_malloc(sizeof(YR_STRUCTURE_MEMBER)); if (sm == NULL) return ERROR_INSUFFICIENT_MEMORY; member->parent = object; sm->object = member; sm->next = object_as_structure(object)->members; object_as_structure(object)->members = sm; return ERROR_SUCCESS; } int yr_object_array_length(YR_OBJECT* object) { YR_OBJECT_ARRAY* array; assert(object->type == OBJECT_TYPE_ARRAY); array = object_as_array(object); if (array->items == NULL) return 0; return array->items->length; } YR_OBJECT* yr_object_array_get_item(YR_OBJECT* object, int flags, int index) { YR_OBJECT* result = NULL; YR_OBJECT_ARRAY* array; assert(object->type == OBJECT_TYPE_ARRAY); if (index < 0) return NULL; array = object_as_array(object); if (array->items != NULL && array->items->capacity > index) result = array->items->objects[index]; if (result == NULL && flags & OBJECT_CREATE) { yr_object_copy(array->prototype_item, &result); if (result != NULL) yr_object_array_set_item(object, result, index); } return result; } int yr_object_array_set_item(YR_OBJECT* object, YR_OBJECT* item, int index) { YR_OBJECT_ARRAY* array; int i; int capacity; assert(index >= 0); assert(object->type == OBJECT_TYPE_ARRAY); array = object_as_array(object); if (array->items == NULL) { capacity = 64; while (capacity <= index) capacity *= 2; array->items = (YR_ARRAY_ITEMS*) yr_malloc( sizeof(YR_ARRAY_ITEMS) + capacity * sizeof(YR_OBJECT*)); if (array->items == NULL) return ERROR_INSUFFICIENT_MEMORY; memset(array->items->objects, 0, capacity * sizeof(YR_OBJECT*)); array->items->capacity = capacity; array->items->length = 0; } else if (index >= array->items->capacity) { capacity = array->items->capacity * 2; while (capacity <= index) capacity *= 2; array->items = (YR_ARRAY_ITEMS*) yr_realloc( array->items, sizeof(YR_ARRAY_ITEMS) + capacity * sizeof(YR_OBJECT*)); if (array->items == NULL) return ERROR_INSUFFICIENT_MEMORY; for (i = array->items->capacity; i < capacity; i++) array->items->objects[i] = NULL; array->items->capacity = capacity; } item->parent = object; array->items->objects[index] = item; if (index >= array->items->length) array->items->length = index + 1; return ERROR_SUCCESS; } YR_OBJECT* yr_object_dict_get_item( YR_OBJECT* object, int flags, const char* key) { int i; YR_OBJECT* result = NULL; YR_OBJECT_DICTIONARY* dict; assert(object->type == OBJECT_TYPE_DICTIONARY); dict = object_as_dictionary(object); if (dict->items != NULL) { for (i = 0; i < dict->items->used; i++) { if (strcmp(dict->items->objects[i].key->c_string, key) == 0) result = dict->items->objects[i].obj; } } if (result == NULL && flags & OBJECT_CREATE) { yr_object_copy(dict->prototype_item, &result); if (result != NULL) yr_object_dict_set_item(object, result, key); } return result; } int yr_object_dict_set_item(YR_OBJECT* object, YR_OBJECT* item, const char* key) { YR_OBJECT_DICTIONARY* dict; int i; int count; assert(object->type == OBJECT_TYPE_DICTIONARY); dict = object_as_dictionary(object); if (dict->items == NULL) { count = 64; dict->items = (YR_DICTIONARY_ITEMS*) yr_malloc( sizeof(YR_DICTIONARY_ITEMS) + count * sizeof(dict->items->objects[0])); if (dict->items == NULL) return ERROR_INSUFFICIENT_MEMORY; memset(dict->items->objects, 0, count * sizeof(dict->items->objects[0])); dict->items->free = count; dict->items->used = 0; } else if (dict->items->free == 0) { count = dict->items->used * 2; dict->items = (YR_DICTIONARY_ITEMS*) yr_realloc( dict->items, sizeof(YR_DICTIONARY_ITEMS) + count * sizeof(dict->items->objects[0])); if (dict->items == NULL) return ERROR_INSUFFICIENT_MEMORY; for (i = dict->items->used; i < count; i++) { dict->items->objects[i].key = NULL; dict->items->objects[i].obj = NULL; } dict->items->free = dict->items->used; } item->parent = object; dict->items->objects[dict->items->used].key = ss_new(key); dict->items->objects[dict->items->used].obj = item; dict->items->used++; dict->items->free--; return ERROR_SUCCESS; } bool yr_object_has_undefined_value(YR_OBJECT* object, const char* field, ...) { YR_OBJECT* field_obj; va_list args; va_start(args, field); if (field != NULL) field_obj = _yr_object_lookup(object, 0, field, args); else field_obj = object; va_end(args); if (field_obj == NULL) return true; switch (field_obj->type) { case OBJECT_TYPE_FLOAT: return isnan(field_obj->value.d); case OBJECT_TYPE_STRING: return field_obj->value.ss == NULL; case OBJECT_TYPE_INTEGER: return field_obj->value.i == YR_UNDEFINED; } return false; } int64_t yr_object_get_integer(YR_OBJECT* object, const char* field, ...) { YR_OBJECT* integer_obj; va_list args; va_start(args, field); if (field != NULL) integer_obj = _yr_object_lookup(object, 0, field, args); else integer_obj = object; va_end(args); if (integer_obj == NULL) return YR_UNDEFINED; assertf( integer_obj->type == OBJECT_TYPE_INTEGER, "type of \"%s\" is not integer\n", field); return integer_obj->value.i; } double yr_object_get_float(YR_OBJECT* object, const char* field, ...) { YR_OBJECT* double_obj; va_list args; va_start(args, field); if (field != NULL) double_obj = _yr_object_lookup(object, 0, field, args); else double_obj = object; va_end(args); if (double_obj == NULL) return NAN; assertf( double_obj->type == OBJECT_TYPE_FLOAT, "type of \"%s\" is not double\n", field); return double_obj->value.d; } SIZED_STRING* yr_object_get_string(YR_OBJECT* object, const char* field, ...) { YR_OBJECT* string_obj; va_list args; va_start(args, field); if (field != NULL) string_obj = _yr_object_lookup(object, 0, field, args); else string_obj = object; va_end(args); if (string_obj == NULL) return NULL; assertf( string_obj->type == OBJECT_TYPE_STRING, "type of \"%s\" is not string\n", field); return string_obj->value.ss; } int yr_object_set_integer( int64_t value, YR_OBJECT* object, const char* field, ...) { YR_OBJECT* integer_obj; va_list args; va_start(args, field); if (field != NULL) integer_obj = _yr_object_lookup(object, OBJECT_CREATE, field, args); else integer_obj = object; va_end(args); if (integer_obj == NULL) { if (field != NULL) return ERROR_INSUFFICIENT_MEMORY; else return ERROR_INVALID_ARGUMENT; } assert(integer_obj->type == OBJECT_TYPE_INTEGER); integer_obj->value.i = value; return ERROR_SUCCESS; } int yr_object_set_float(double value, YR_OBJECT* object, const char* field, ...) { YR_OBJECT* double_obj; va_list args; va_start(args, field); if (field != NULL) double_obj = _yr_object_lookup(object, OBJECT_CREATE, field, args); else double_obj = object; va_end(args); if (double_obj == NULL) { if (field != NULL) return ERROR_INSUFFICIENT_MEMORY; else return ERROR_INVALID_ARGUMENT; } assert(double_obj->type == OBJECT_TYPE_FLOAT); double_obj->value.d = value; return ERROR_SUCCESS; } int yr_object_set_string( const char* value, size_t len, YR_OBJECT* object, const char* field, ...) { YR_OBJECT* string_obj; va_list args; va_start(args, field); if (field != NULL) string_obj = _yr_object_lookup(object, OBJECT_CREATE, field, args); else string_obj = object; va_end(args); if (string_obj == NULL) { if (field != NULL) return ERROR_INSUFFICIENT_MEMORY; else return ERROR_INVALID_ARGUMENT; } assert(string_obj->type == OBJECT_TYPE_STRING); if (string_obj->value.ss != NULL) yr_free(string_obj->value.ss); if (value != NULL) { string_obj->value.ss = (SIZED_STRING*) yr_malloc( len + sizeof(SIZED_STRING)); if (string_obj->value.ss == NULL) return ERROR_INSUFFICIENT_MEMORY; string_obj->value.ss->length = (uint32_t) len; string_obj->value.ss->flags = 0; memcpy(string_obj->value.ss->c_string, value, len); string_obj->value.ss->c_string[len] = '\0'; } else { string_obj->value.ss = NULL; } return ERROR_SUCCESS; } YR_OBJECT* yr_object_get_root(YR_OBJECT* object) { YR_OBJECT* o = object; while (o->parent != NULL) o = o->parent; return o; } YR_API void yr_object_print_data( YR_OBJECT* object, int indent, int print_identifier) { YR_DICTIONARY_ITEMS* dict_items; YR_STRUCTURE_MEMBER* member; char indent_spaces[32]; int i; indent = yr_min(indent, sizeof(indent_spaces) - 1); memset(indent_spaces, '\t', indent); indent_spaces[indent] = '\0'; if (print_identifier && object->type != OBJECT_TYPE_FUNCTION) printf("%s%s", indent_spaces, object->identifier); switch (object->type) { case OBJECT_TYPE_FLOAT: if (object->value.i != YR_UNDEFINED) printf(" = %f", object->value.d); else printf(" = YR_UNDEFINED"); break; case OBJECT_TYPE_INTEGER: if (object->value.i != YR_UNDEFINED) printf(" = %" PRId64, object->value.i); else printf(" = YR_UNDEFINED"); break; case OBJECT_TYPE_STRING: if (object->value.ss != NULL) { size_t l; printf(" = \""); for (l = 0; l < object->value.ss->length; l++) { char c = object->value.ss->c_string[l]; if (isprint((unsigned char) c)) printf("%c", c); else printf("\\x%02x", (unsigned char) c); } printf("\""); } else { printf(" = YR_UNDEFINED"); } break; case OBJECT_TYPE_STRUCTURE: member = object_as_structure(object)->members; while (member != NULL) { if (member->object->type != OBJECT_TYPE_FUNCTION) { printf("\n"); yr_object_print_data(member->object, indent + 1, 1); } member = member->next; } break; case OBJECT_TYPE_ARRAY: for (i = 0; i < yr_object_array_length(object); i++) { YR_OBJECT* o = yr_object_array_get_item(object, 0, i); if (o != NULL) { printf("\n%s\t[%d]", indent_spaces, i); yr_object_print_data(o, indent + 1, 0); } } break; case OBJECT_TYPE_DICTIONARY: dict_items = object_as_dictionary(object)->items; if (dict_items != NULL) { for (i = 0; i < dict_items->used; i++) { printf("\n%s\t%s", indent_spaces, dict_items->objects[i].key->c_string); yr_object_print_data(dict_items->objects[i].obj, indent + 1, 0); } } break; } } yara-4.1.3/libyara/parser.c000066400000000000000000001102141413423160300155320ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define todigit(x) \ ((x) >= 'A' && (x) <= 'F') ? ((uint8_t)(x - 'A' + 10)) : ((uint8_t)(x - '0')) int yr_parser_emit( yyscan_t yyscanner, uint8_t instruction, YR_ARENA_REF* instruction_ref) { return yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &instruction, sizeof(uint8_t), instruction_ref); } int yr_parser_emit_with_arg_double( yyscan_t yyscanner, uint8_t instruction, double argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { int result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &instruction, sizeof(uint8_t), instruction_ref); if (result == ERROR_SUCCESS) result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &argument, sizeof(double), argument_ref); return result; } int yr_parser_emit_with_arg_int32( yyscan_t yyscanner, uint8_t instruction, int32_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { int result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &instruction, sizeof(uint8_t), instruction_ref); if (result == ERROR_SUCCESS) result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &argument, sizeof(int32_t), argument_ref); return result; } int yr_parser_emit_with_arg( yyscan_t yyscanner, uint8_t instruction, int64_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { int result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &instruction, sizeof(uint8_t), instruction_ref); if (result == ERROR_SUCCESS) result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &argument, sizeof(int64_t), argument_ref); return result; } int yr_parser_emit_with_arg_reloc( yyscan_t yyscanner, uint8_t instruction, void* argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { YR_ARENA_REF ref = YR_ARENA_NULL_REF; DECLARE_REFERENCE(void*, ptr) arg; memset(&arg, 0, sizeof(arg)); arg.ptr = argument; int result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &instruction, sizeof(uint8_t), instruction_ref); if (result == ERROR_SUCCESS) result = yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, &arg, sizeof(arg), &ref); if (result == ERROR_SUCCESS) result = yr_arena_make_ptr_relocatable( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, ref.offset, EOL); if (argument_ref != NULL) *argument_ref = ref; return result; } int yr_parser_emit_pushes_for_strings( yyscan_t yyscanner, const char* identifier) { YR_COMPILER* compiler = yyget_extra(yyscanner); YR_RULE* current_rule = _yr_compiler_get_rule_by_idx( compiler, compiler->current_rule_idx); YR_STRING* string; const char* string_identifier; const char* target_identifier; int matching = 0; yr_rule_strings_foreach(current_rule, string) { // Don't generate pushes for strings chained to another one, we are // only interested in non-chained strings or the head of the chain. if (string->chained_to == NULL) { string_identifier = string->identifier; target_identifier = identifier; while (*target_identifier != '\0' && *string_identifier != '\0' && *target_identifier == *string_identifier) { target_identifier++; string_identifier++; } if ((*target_identifier == '\0' && *string_identifier == '\0') || *target_identifier == '*') { yr_parser_emit_with_arg_reloc(yyscanner, OP_PUSH, string, NULL, NULL); string->flags |= STRING_FLAGS_REFERENCED; string->flags &= ~STRING_FLAGS_FIXED_OFFSET; matching++; } } } if (matching == 0) { yr_compiler_set_error_extra_info( compiler, identifier) return ERROR_UNDEFINED_STRING; } return ERROR_SUCCESS; } int yr_parser_emit_push_const(yyscan_t yyscanner, uint64_t argument) { uint64_t u = (uint64_t) argument; uint8_t buf[9]; int bufsz = 1; if (u == YR_UNDEFINED) { buf[0] = OP_PUSH_U; } else if (u <= 0xff) { buf[0] = OP_PUSH_8; bufsz += sizeof(uint8_t); buf[1] = (uint8_t) argument; } else if (u <= 0xffff) { buf[0] = OP_PUSH_16; bufsz += sizeof(uint16_t); *((uint16_t*) (buf + 1)) = (uint16_t) argument; } else if (u <= 0xffffffff) { buf[0] = OP_PUSH_32; bufsz += sizeof(uint32_t); *((uint32_t*) (buf + 1)) = (uint32_t) argument; } else { buf[0] = OP_PUSH; bufsz += sizeof(uint64_t); *((uint64_t*) (buf + 1)) = (uint64_t) argument; } return yr_arena_write_data( yyget_extra(yyscanner)->arena, YR_CODE_SECTION, buf, bufsz, NULL); } int yr_parser_check_types( YR_COMPILER* compiler, YR_OBJECT_FUNCTION* function, const char* actual_args_fmt) { int i; for (i = 0; i < YR_MAX_OVERLOADED_FUNCTIONS; i++) { if (function->prototypes[i].arguments_fmt == NULL) break; if (strcmp(function->prototypes[i].arguments_fmt, actual_args_fmt) == 0) return ERROR_SUCCESS; } yr_compiler_set_error_extra_info(compiler, function->identifier) return ERROR_WRONG_ARGUMENTS; } int yr_parser_lookup_string( yyscan_t yyscanner, const char* identifier, YR_STRING** string) { YR_COMPILER* compiler = yyget_extra(yyscanner); YR_RULE* current_rule = _yr_compiler_get_rule_by_idx( compiler, compiler->current_rule_idx); yr_rule_strings_foreach(current_rule, *string) { // If some string $a gets fragmented into multiple chained // strings, all those fragments have the same $a identifier // but we are interested in the heading fragment, which is // that with chained_to == NULL if ((*string)->chained_to == NULL && strcmp((*string)->identifier, identifier) == 0) { return ERROR_SUCCESS; } } yr_compiler_set_error_extra_info(compiler, identifier) * string = NULL; return ERROR_UNDEFINED_STRING; } //////////////////////////////////////////////////////////////////////////////// // Searches for a variable with the given identifier in the scope of the current // "for" loop. In case of nested "for" loops the identifier is searched starting // at the top-level loop and going down thorough the nested loops until the // current one. This is ok because inner loops can not re-define an identifier // already defined by an outer loop. // // If the variable is found, the return value is the position that the variable // occupies among all the currently defined variables. If the variable doesn't // exist the return value is -1. // // The function can receive a pointer to a YR_EXPRESSION that will populated // with information about the variable if found. This pointer can be NULL if // the caller is not interested in getting that information. // int yr_parser_lookup_loop_variable( yyscan_t yyscanner, const char* identifier, YR_EXPRESSION* expr) { YR_COMPILER* compiler = yyget_extra(yyscanner); int i, j; int var_offset = 0; for (i = 0; i <= compiler->loop_index; i++) { var_offset += compiler->loop[i].vars_internal_count; for (j = 0; j < compiler->loop[i].vars_count; j++) { if (compiler->loop[i].vars[j].identifier.ptr != NULL && strcmp(identifier, compiler->loop[i].vars[j].identifier.ptr) == 0) { if (expr != NULL) *expr = compiler->loop[i].vars[j]; return var_offset + j; } } var_offset += compiler->loop[i].vars_count; } return -1; } static int _yr_parser_write_string( const char* identifier, YR_MODIFIER modifier, YR_COMPILER* compiler, SIZED_STRING* str, RE_AST* re_ast, YR_ARENA_REF* string_ref, int* min_atom_quality, int* num_atom) { SIZED_STRING* literal_string; YR_ATOM_LIST_ITEM* atom; YR_ATOM_LIST_ITEM* atom_list = NULL; int c, result; int max_string_len; bool free_literal = false; FAIL_ON_ERROR(yr_arena_allocate_struct( compiler->arena, YR_STRINGS_TABLE, sizeof(YR_STRING), string_ref, offsetof(YR_STRING, identifier), offsetof(YR_STRING, string), offsetof(YR_STRING, chained_to), EOL)); YR_STRING* string = (YR_STRING*) yr_arena_ref_to_ptr( compiler->arena, string_ref); YR_ARENA_REF ref; FAIL_ON_ERROR(_yr_compiler_store_string(compiler, identifier, &ref)); string->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref); if (modifier.flags & STRING_FLAGS_HEXADECIMAL || modifier.flags & STRING_FLAGS_REGEXP || modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE) { literal_string = yr_re_ast_extract_literal(re_ast); if (literal_string != NULL) { modifier.flags |= STRING_FLAGS_LITERAL; free_literal = true; } else { // Non-literal strings can't be marked as fixed offset because once we // find a string atom in the scanned data we don't know the offset where // the string should start, as the non-literal strings can contain // variable-length portions. modifier.flags &= ~STRING_FLAGS_FIXED_OFFSET; } } else { literal_string = str; modifier.flags |= STRING_FLAGS_LITERAL; } string->flags = modifier.flags; string->rule_idx = compiler->current_rule_idx; string->idx = compiler->current_string_idx; string->fixed_offset = YR_UNDEFINED; string->chained_to = NULL; string->string = NULL; if (modifier.flags & STRING_FLAGS_LITERAL) { result = _yr_compiler_store_data( compiler, literal_string->c_string, literal_string->length + 1, // +1 to include terminating NULL &ref); string->length = (uint32_t) literal_string->length; string->string = (uint8_t*) yr_arena_ref_to_ptr(compiler->arena, &ref); if (result == ERROR_SUCCESS) { result = yr_atoms_extract_from_string( &compiler->atoms_config, (uint8_t*) literal_string->c_string, (int32_t) literal_string->length, modifier, &atom_list, min_atom_quality); } } else { // Emit forwards code result = yr_re_ast_emit_code(re_ast, compiler->arena, false); // Emit backwards code if (result == ERROR_SUCCESS) result = yr_re_ast_emit_code(re_ast, compiler->arena, true); if (result == ERROR_SUCCESS) result = yr_atoms_extract_from_re( &compiler->atoms_config, re_ast, modifier, &atom_list, min_atom_quality); } if (result == ERROR_SUCCESS) { // Add the string to Aho-Corasick automaton. result = yr_ac_add_string( compiler->automaton, string, compiler->current_string_idx, atom_list, compiler->arena); } if (modifier.flags & STRING_FLAGS_LITERAL) { if (modifier.flags & STRING_FLAGS_WIDE) max_string_len = string->length * 2; else max_string_len = string->length; if (max_string_len <= YR_MAX_ATOM_LENGTH) string->flags |= STRING_FLAGS_FITS_IN_ATOM; } atom = atom_list; c = 0; while (atom != NULL) { atom = atom->next; c++; } (*num_atom) += c; compiler->current_string_idx++; if (free_literal) yr_free(literal_string); if (atom_list != NULL) yr_atoms_list_destroy(atom_list); return result; } int yr_parser_reduce_string_declaration( yyscan_t yyscanner, YR_MODIFIER modifier, const char* identifier, SIZED_STRING* str, YR_ARENA_REF* string_ref) { int result = ERROR_SUCCESS; int min_atom_quality = YR_MAX_ATOM_QUALITY; int atom_quality; char message[512]; int32_t min_gap = 0; int32_t max_gap = 0; YR_COMPILER* compiler = yyget_extra(yyscanner); RE_AST* re_ast = NULL; RE_AST* remainder_re_ast = NULL; RE_ERROR re_error; YR_RULE* current_rule = _yr_compiler_get_rule_by_idx( compiler, compiler->current_rule_idx); // Determine if a string with the same identifier was already defined // by searching for the identifier in strings_table. uint32_t string_idx = yr_hash_table_lookup_uint32( compiler->strings_table, identifier, NULL); // The string was already defined, return an error. if (string_idx != UINT32_MAX) { result = ERROR_DUPLICATED_STRING_IDENTIFIER; yr_compiler_set_error_extra_info(compiler, identifier) goto _exit; } // Empty strings are not allowed. if (str->length == 0) { result = ERROR_EMPTY_STRING; yr_compiler_set_error_extra_info(compiler, identifier) goto _exit; } // If string identifier is $ this is an anonymous string, if not add the // identifier to strings_table. if (strcmp(identifier, "$") == 0) { modifier.flags |= STRING_FLAGS_ANONYMOUS; } else { result = yr_hash_table_add_uint32( compiler->strings_table, identifier, NULL, compiler->current_string_idx); if (result != ERROR_SUCCESS) goto _exit; } if (str->flags & SIZED_STRING_FLAGS_NO_CASE) modifier.flags |= STRING_FLAGS_NO_CASE; if (str->flags & SIZED_STRING_FLAGS_DOT_ALL) modifier.flags |= STRING_FLAGS_DOT_ALL; // Hex strings are always handled as DOT_ALL regexps. if (modifier.flags & STRING_FLAGS_HEXADECIMAL) modifier.flags |= STRING_FLAGS_DOT_ALL; // xor and nocase together is not implemented. if (modifier.flags & STRING_FLAGS_XOR && modifier.flags & STRING_FLAGS_NO_CASE) { result = ERROR_INVALID_MODIFIER; yr_compiler_set_error_extra_info( compiler, "invalid modifier combination: xor nocase") goto _exit; } // base64 and nocase together is not implemented. if (modifier.flags & STRING_FLAGS_NO_CASE && (modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE)) { result = ERROR_INVALID_MODIFIER; yr_compiler_set_error_extra_info( compiler, modifier.flags & STRING_FLAGS_BASE64 ? "invalid modifier combination: base64 nocase" : "invalid modifier combination: base64wide nocase") goto _exit; } // base64 and fullword together is not implemented. if (modifier.flags & STRING_FLAGS_FULL_WORD && (modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE)) { result = ERROR_INVALID_MODIFIER; yr_compiler_set_error_extra_info( compiler, modifier.flags & STRING_FLAGS_BASE64 ? "invalid modifier combination: base64 fullword" : "invalid modifier combination: base64wide fullword") goto _exit; } // base64 and xor together is not implemented. if (modifier.flags & STRING_FLAGS_XOR && (modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE)) { result = ERROR_INVALID_MODIFIER; yr_compiler_set_error_extra_info( compiler, modifier.flags & STRING_FLAGS_BASE64 ? "invalid modifier combination: base64 xor" : "invalid modifier combination: base64wide xor") goto _exit; } if (!(modifier.flags & STRING_FLAGS_WIDE) && !(modifier.flags & STRING_FLAGS_XOR) && !(modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE)) { modifier.flags |= STRING_FLAGS_ASCII; } // The STRING_FLAGS_SINGLE_MATCH flag indicates that finding // a single match for the string is enough. This is true in // most cases, except when the string count (#) and string offset (@) // operators are used. All strings are marked STRING_FLAGS_SINGLE_MATCH // initially, and unmarked later if required. modifier.flags |= STRING_FLAGS_SINGLE_MATCH; // The STRING_FLAGS_FIXED_OFFSET indicates that the string doesn't // need to be searched all over the file because the user is using the // "at" operator. The string must be searched at a fixed offset in the // file. All strings are marked STRING_FLAGS_FIXED_OFFSET initially, // and unmarked later if required. modifier.flags |= STRING_FLAGS_FIXED_OFFSET; if (modifier.flags & STRING_FLAGS_HEXADECIMAL || modifier.flags & STRING_FLAGS_REGEXP || modifier.flags & STRING_FLAGS_BASE64 || modifier.flags & STRING_FLAGS_BASE64_WIDE) { if (modifier.flags & STRING_FLAGS_HEXADECIMAL) result = yr_re_parse_hex(str->c_string, &re_ast, &re_error); else if (modifier.flags & STRING_FLAGS_REGEXP) result = yr_re_parse(str->c_string, &re_ast, &re_error); else result = yr_base64_ast_from_string(str, modifier, &re_ast, &re_error); if (result != ERROR_SUCCESS) { snprintf( message, sizeof(message), "invalid %s \"%s\": %s", (modifier.flags & STRING_FLAGS_HEXADECIMAL) ? "hex string" : "regular expression", identifier, re_error.message); yr_compiler_set_error_extra_info(compiler, message) goto _exit; } if (re_ast->flags & RE_FLAGS_FAST_REGEXP) modifier.flags |= STRING_FLAGS_FAST_REGEXP; if (re_ast->flags & RE_FLAGS_GREEDY) modifier.flags |= STRING_FLAGS_GREEDY_REGEXP; // Regular expressions in the strings section can't mix greedy and ungreedy // quantifiers like .* and .*?. That's because these regular expressions can // be matched forwards and/or backwards depending on the atom found, and we // need the regexp to be all-greedy or all-ungreedy to be able to properly // calculate the length of the match. if ((re_ast->flags & RE_FLAGS_GREEDY) && (re_ast->flags & RE_FLAGS_UNGREEDY)) { result = ERROR_INVALID_REGULAR_EXPRESSION; yr_compiler_set_error_extra_info( compiler, "greedy and ungreedy quantifiers can't be mixed in a regular " "expression") goto _exit; } if (yr_re_ast_has_unbounded_quantifier_for_dot(re_ast)) { yywarning( yyscanner, "%s contains .*, .+ or .{x,} consider using .{,N}, .{1,N} or {x,N} " "with a reasonable value for N", identifier); } if (compiler->re_ast_callback != NULL) { compiler->re_ast_callback( current_rule, identifier, re_ast, compiler->re_ast_clbk_user_data); } *string_ref = YR_ARENA_NULL_REF; while (re_ast != NULL) { YR_ARENA_REF ref; uint32_t prev_string_idx = compiler->current_string_idx - 1; int32_t prev_min_gap = min_gap; int32_t prev_max_gap = max_gap; result = yr_re_ast_split_at_chaining_point( re_ast, &remainder_re_ast, &min_gap, &max_gap); if (result != ERROR_SUCCESS) goto _exit; result = _yr_parser_write_string( identifier, modifier, compiler, NULL, re_ast, &ref, &atom_quality, ¤t_rule->num_atoms); if (result != ERROR_SUCCESS) goto _exit; if (atom_quality < min_atom_quality) min_atom_quality = atom_quality; if (YR_ARENA_IS_NULL_REF(*string_ref)) { // This is the first string in the chain, the string reference returned // by this function must point to this string. *string_ref = ref; } else { // This is not the first string in the chain, set the appropriate flags // and fill the chained_to, chain_gap_min and chain_gap_max fields. YR_STRING* prev_string = (YR_STRING*) yr_arena_get_ptr( compiler->arena, YR_STRINGS_TABLE, prev_string_idx * sizeof(YR_STRING)); YR_STRING* new_string = (YR_STRING*) yr_arena_ref_to_ptr( compiler->arena, &ref); new_string->chained_to = prev_string; new_string->chain_gap_min = prev_min_gap; new_string->chain_gap_max = prev_max_gap; // A string chained to another one can't have a fixed offset, only the // head of the string chain can have a fixed offset. new_string->flags &= ~STRING_FLAGS_FIXED_OFFSET; // There is a previous string, but that string wasn't marked as part of // a chain because we can't do that until knowing there will be another // string, let's flag it now the we know. prev_string->flags |= STRING_FLAGS_CHAIN_PART; // There is a previous string, so this string is part of a chain, but // there will be no more strings because there are no more AST to split, // which means that this is the chain's tail. if (remainder_re_ast == NULL) new_string->flags |= STRING_FLAGS_CHAIN_PART | STRING_FLAGS_CHAIN_TAIL; } yr_re_ast_destroy(re_ast); re_ast = remainder_re_ast; } } else // not a STRING_FLAGS_HEXADECIMAL or STRING_FLAGS_REGEXP or // STRING_FLAGS_BASE64 or STRING_FLAGS_BASE64_WIDE { result = _yr_parser_write_string( identifier, modifier, compiler, str, NULL, string_ref, &min_atom_quality, ¤t_rule->num_atoms); if (result != ERROR_SUCCESS) goto _exit; } if (min_atom_quality < compiler->atoms_config.quality_warning_threshold) { yywarning(yyscanner, "string \"%s\" may slow down scanning", identifier); } _exit: if (re_ast != NULL) yr_re_ast_destroy(re_ast); if (remainder_re_ast != NULL) yr_re_ast_destroy(remainder_re_ast); return result; } int yr_parser_reduce_rule_declaration_phase_1( yyscan_t yyscanner, int32_t flags, const char* identifier, YR_ARENA_REF* rule_ref) { YR_FIXUP* fixup; YR_COMPILER* compiler = yyget_extra(yyscanner); YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE)); if (yr_hash_table_lookup_uint32( compiler->rules_table, identifier, ns->name) != UINT32_MAX || yr_hash_table_lookup(compiler->objects_table, identifier, NULL) != NULL) { // A rule or variable with the same identifier already exists, return the // appropriate error. yr_compiler_set_error_extra_info( compiler, identifier) return ERROR_DUPLICATED_IDENTIFIER; } FAIL_ON_ERROR(yr_arena_allocate_struct( compiler->arena, YR_RULES_TABLE, sizeof(YR_RULE), rule_ref, offsetof(YR_RULE, identifier), offsetof(YR_RULE, tags), offsetof(YR_RULE, strings), offsetof(YR_RULE, metas), offsetof(YR_RULE, ns), EOL)); YR_RULE* rule = (YR_RULE*) yr_arena_ref_to_ptr(compiler->arena, rule_ref); YR_ARENA_REF ref; FAIL_ON_ERROR(_yr_compiler_store_string(compiler, identifier, &ref)); rule->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref); rule->flags = flags; rule->ns = ns; rule->num_atoms = 0; YR_ARENA_REF jmp_offset_ref; // We are starting to parse a new rule, set current_rule_idx accordingly. compiler->current_rule_idx = compiler->next_rule_idx; compiler->next_rule_idx++; // The OP_INIT_RULE instruction behaves like a jump. When the rule is disabled // it skips over the rule's code and go straight to the next rule's code. The // jmp_offset_ref variable points to the jump's offset. The offset is set to 0 // as we don't know the jump target yet. When we finish generating the rule's // code in yr_parser_reduce_rule_declaration_phase_2 the jump offset is set to // its final value. FAIL_ON_ERROR(yr_parser_emit_with_arg_int32( yyscanner, OP_INIT_RULE, 0, NULL, &jmp_offset_ref)); FAIL_ON_ERROR(yr_arena_write_data( compiler->arena, YR_CODE_SECTION, &compiler->current_rule_idx, sizeof(compiler->current_rule_idx), NULL)); // Create a fixup entry for the jump and push it in the stack fixup = (YR_FIXUP*) yr_malloc(sizeof(YR_FIXUP)); if (fixup == NULL) return ERROR_INSUFFICIENT_MEMORY; fixup->ref = jmp_offset_ref; fixup->next = compiler->fixup_stack_head; compiler->fixup_stack_head = fixup; // Clean strings_table as we are starting to parse a new rule. yr_hash_table_clean(compiler->strings_table, NULL); FAIL_ON_ERROR(yr_hash_table_add_uint32( compiler->rules_table, identifier, ns->name, compiler->current_rule_idx)); return ERROR_SUCCESS; } int yr_parser_reduce_rule_declaration_phase_2( yyscan_t yyscanner, YR_ARENA_REF* rule_ref) { uint32_t max_strings_per_rule; uint32_t strings_in_rule = 0; YR_FIXUP* fixup; YR_STRING* string; YR_COMPILER* compiler = yyget_extra(yyscanner); yr_get_configuration( YR_CONFIG_MAX_STRINGS_PER_RULE, (void*) &max_strings_per_rule); YR_RULE* rule = (YR_RULE*) yr_arena_ref_to_ptr(compiler->arena, rule_ref); // Show warning if the rule is generating too many atoms. The warning is // shown if the number of atoms is greater than 20 times the maximum number // of strings allowed for a rule, as 20 is minimum number of atoms generated // for a string using *nocase*, *ascii* and *wide* modifiers simultaneously. if (rule->num_atoms > YR_ATOMS_PER_RULE_WARNING_THRESHOLD) { yywarning(yyscanner, "rule is slowing down scanning"); } yr_rule_strings_foreach(rule, string) { // Only the heading fragment in a chain of strings (the one with // chained_to == NULL) must be referenced. All other fragments // are never marked as referenced. if (!STRING_IS_REFERENCED(string) && string->chained_to == NULL) { yr_compiler_set_error_extra_info( compiler, string->identifier) return ERROR_UNREFERENCED_STRING; } strings_in_rule++; if (strings_in_rule > max_strings_per_rule) { yr_compiler_set_error_extra_info( compiler, rule->identifier) return ERROR_TOO_MANY_STRINGS; } } FAIL_ON_ERROR(yr_parser_emit_with_arg( yyscanner, OP_MATCH_RULE, compiler->current_rule_idx, NULL, NULL)); fixup = compiler->fixup_stack_head; int32_t* jmp_offset_addr = (int32_t*) yr_arena_ref_to_ptr( compiler->arena, &fixup->ref); int32_t jmp_offset = yr_arena_get_current_offset( compiler->arena, YR_CODE_SECTION) - fixup->ref.offset + 1; *jmp_offset_addr = jmp_offset; // Remove fixup from the stack. compiler->fixup_stack_head = fixup->next; yr_free(fixup); // We have finished parsing the current rule set current_rule_idx to // UINT32_MAX indicating that we are not currently parsing a rule. compiler->current_rule_idx = UINT32_MAX; return ERROR_SUCCESS; } int yr_parser_reduce_string_identifier( yyscan_t yyscanner, const char* identifier, uint8_t instruction, uint64_t at_offset) { YR_STRING* string; YR_COMPILER* compiler = yyget_extra(yyscanner); if (strcmp(identifier, "$") == 0) // is an anonymous string ? { if (compiler->loop_for_of_var_index >= 0) // inside a loop ? { yr_parser_emit_with_arg( yyscanner, OP_PUSH_M, compiler->loop_for_of_var_index, NULL, NULL); yr_parser_emit(yyscanner, instruction, NULL); YR_RULE* current_rule = _yr_compiler_get_rule_by_idx( compiler, compiler->current_rule_idx); yr_rule_strings_foreach(current_rule, string) { if (instruction != OP_FOUND) string->flags &= ~STRING_FLAGS_SINGLE_MATCH; if (instruction == OP_FOUND_AT) { // Avoid overwriting any previous fixed offset if (string->fixed_offset == YR_UNDEFINED) string->fixed_offset = at_offset; // If a previous fixed offset was different, disable // the STRING_GFLAGS_FIXED_OFFSET flag because we only // have room to store a single fixed offset value if (string->fixed_offset != at_offset) string->flags &= ~STRING_FLAGS_FIXED_OFFSET; } else { string->flags &= ~STRING_FLAGS_FIXED_OFFSET; } } } else { // Anonymous strings not allowed outside of a loop return ERROR_MISPLACED_ANONYMOUS_STRING; } } else { FAIL_ON_ERROR(yr_parser_lookup_string(yyscanner, identifier, &string)); FAIL_ON_ERROR( yr_parser_emit_with_arg_reloc(yyscanner, OP_PUSH, string, NULL, NULL)); if (instruction != OP_FOUND) string->flags &= ~STRING_FLAGS_SINGLE_MATCH; if (instruction == OP_FOUND_AT) { // Avoid overwriting any previous fixed offset if (string->fixed_offset == YR_UNDEFINED) string->fixed_offset = at_offset; // If a previous fixed offset was different, disable // the STRING_GFLAGS_FIXED_OFFSET flag because we only // have room to store a single fixed offset value if (string->fixed_offset == YR_UNDEFINED || string->fixed_offset != at_offset) { string->flags &= ~STRING_FLAGS_FIXED_OFFSET; } } else { string->flags &= ~STRING_FLAGS_FIXED_OFFSET; } FAIL_ON_ERROR(yr_parser_emit(yyscanner, instruction, NULL)); string->flags |= STRING_FLAGS_REFERENCED; } return ERROR_SUCCESS; } int yr_parser_reduce_meta_declaration( yyscan_t yyscanner, int32_t type, const char* identifier, const char* string, int64_t integer, YR_ARENA_REF* meta_ref) { YR_ARENA_REF ref; YR_COMPILER* compiler = yyget_extra(yyscanner); FAIL_ON_ERROR(yr_arena_allocate_struct( compiler->arena, YR_METAS_TABLE, sizeof(YR_META), meta_ref, offsetof(YR_META, identifier), offsetof(YR_META, string), EOL)); YR_META* meta = (YR_META*) yr_arena_ref_to_ptr(compiler->arena, meta_ref); meta->type = type; meta->integer = integer; FAIL_ON_ERROR(_yr_compiler_store_string(compiler, identifier, &ref)); meta->identifier = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref); if (string != NULL) { FAIL_ON_ERROR(_yr_compiler_store_string(compiler, string, &ref)); meta->string = (const char*) yr_arena_ref_to_ptr(compiler->arena, &ref); } else { meta->string = NULL; } compiler->current_meta_idx++; return ERROR_SUCCESS; } static int _yr_parser_valid_module_name(SIZED_STRING* module_name) { if (module_name->length == 0) return false; if (strlen(module_name->c_string) != module_name->length) return false; return true; } int yr_parser_reduce_import(yyscan_t yyscanner, SIZED_STRING* module_name) { int result; YR_ARENA_REF ref; YR_COMPILER* compiler = yyget_extra(yyscanner); YR_OBJECT* module_structure; if (!_yr_parser_valid_module_name(module_name)) { yr_compiler_set_error_extra_info(compiler, module_name->c_string); return ERROR_INVALID_MODULE_NAME; } YR_NAMESPACE* ns = (YR_NAMESPACE*) yr_arena_get_ptr( compiler->arena, YR_NAMESPACES_TABLE, compiler->current_namespace_idx * sizeof(struct YR_NAMESPACE)); module_structure = (YR_OBJECT*) yr_hash_table_lookup( compiler->objects_table, module_name->c_string, ns->name); // if module already imported, do nothing if (module_structure != NULL) return ERROR_SUCCESS; FAIL_ON_ERROR(yr_object_create( OBJECT_TYPE_STRUCTURE, module_name->c_string, NULL, &module_structure)); FAIL_ON_ERROR(yr_hash_table_add( compiler->objects_table, module_name->c_string, ns->name, module_structure)); result = yr_modules_do_declarations(module_name->c_string, module_structure); if (result == ERROR_UNKNOWN_MODULE) yr_compiler_set_error_extra_info(compiler, module_name->c_string); if (result != ERROR_SUCCESS) return result; FAIL_ON_ERROR( _yr_compiler_store_string(compiler, module_name->c_string, &ref)); FAIL_ON_ERROR(yr_parser_emit_with_arg_reloc( yyscanner, OP_IMPORT, yr_arena_ref_to_ptr(compiler->arena, &ref), NULL, NULL)); return ERROR_SUCCESS; } static int _yr_parser_operator_to_opcode(const char* op, int expression_type) { int opcode = 0; switch (expression_type) { case EXPRESSION_TYPE_INTEGER: opcode = OP_INT_BEGIN; break; case EXPRESSION_TYPE_FLOAT: opcode = OP_DBL_BEGIN; break; case EXPRESSION_TYPE_STRING: opcode = OP_STR_BEGIN; break; default: assert(false); } if (op[0] == '<') { if (op[1] == '=') opcode += _OP_LE; else opcode += _OP_LT; } else if (op[0] == '>') { if (op[1] == '=') opcode += _OP_GE; else opcode += _OP_GT; } else if (op[1] == '=') { if (op[0] == '=') opcode += _OP_EQ; else opcode += _OP_NEQ; } else if (op[0] == '+') { opcode += _OP_ADD; } else if (op[0] == '-') { opcode += _OP_SUB; } else if (op[0] == '*') { opcode += _OP_MUL; } else if (op[0] == '\\') { opcode += _OP_DIV; } if (IS_INT_OP(opcode) || IS_DBL_OP(opcode) || IS_STR_OP(opcode)) { return opcode; } return OP_ERROR; } int yr_parser_reduce_operation( yyscan_t yyscanner, const char* op, YR_EXPRESSION left_operand, YR_EXPRESSION right_operand) { int expression_type; YR_COMPILER* compiler = yyget_extra(yyscanner); if ((left_operand.type == EXPRESSION_TYPE_INTEGER || left_operand.type == EXPRESSION_TYPE_FLOAT) && (right_operand.type == EXPRESSION_TYPE_INTEGER || right_operand.type == EXPRESSION_TYPE_FLOAT)) { if (left_operand.type != right_operand.type) { // One operand is double and the other is integer, // cast the integer to double FAIL_ON_ERROR(yr_parser_emit_with_arg( yyscanner, OP_INT_TO_DBL, (left_operand.type == EXPRESSION_TYPE_INTEGER) ? 2 : 1, NULL, NULL)); } expression_type = EXPRESSION_TYPE_FLOAT; if (left_operand.type == EXPRESSION_TYPE_INTEGER && right_operand.type == EXPRESSION_TYPE_INTEGER) { expression_type = EXPRESSION_TYPE_INTEGER; } FAIL_ON_ERROR(yr_parser_emit( yyscanner, _yr_parser_operator_to_opcode(op, expression_type), NULL)); } else if ( left_operand.type == EXPRESSION_TYPE_STRING && right_operand.type == EXPRESSION_TYPE_STRING) { int opcode = _yr_parser_operator_to_opcode(op, EXPRESSION_TYPE_STRING); if (opcode != OP_ERROR) { FAIL_ON_ERROR(yr_parser_emit(yyscanner, opcode, NULL)); } else { yr_compiler_set_error_extra_info_fmt( compiler, "strings don't support \"%s\" operation", op); return ERROR_WRONG_TYPE; } } else { yr_compiler_set_error_extra_info(compiler, "type mismatch"); return ERROR_WRONG_TYPE; } return ERROR_SUCCESS; } yara-4.1.3/libyara/pb/000077500000000000000000000000001413423160300144745ustar00rootroot00000000000000yara-4.1.3/libyara/pb/yara.proto000066400000000000000000000011621413423160300165150ustar00rootroot00000000000000syntax = "proto3"; package yara; import "google/protobuf/descriptor.proto"; message ModuleOptions { string name = 1; string root_message = 2; } message FieldOptions { string name = 1; bool ignore = 2; } message MessageOptions { string name = 1; } message EnumOptions { string name = 1; } extend google.protobuf.FileOptions { ModuleOptions module_options = 51503; } extend google.protobuf.FieldOptions { FieldOptions field_options = 51504; } extend google.protobuf.MessageOptions { MessageOptions message_options = 51505; } extend google.protobuf.EnumOptions { EnumOptions enum_options = 51506; } yara-4.1.3/libyara/proc.c000066400000000000000000000065071413423160300152120ustar00rootroot00000000000000/* Copyright (c) 2007-2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include int _yr_process_attach(int, YR_PROC_ITERATOR_CTX*); int _yr_process_detach(YR_PROC_ITERATOR_CTX*); YR_API int yr_process_open_iterator(int pid, YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_DEBUG_FPRINTF(2, stderr, "+ %s(pid=%d) {\n", __FUNCTION__, pid); int result = ERROR_INTERNAL_FATAL_ERROR; YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) yr_malloc( sizeof(YR_PROC_ITERATOR_CTX)); if (context == NULL) { result = ERROR_INSUFFICIENT_MEMORY; goto _exit; } iterator->context = context; iterator->first = yr_process_get_first_memory_block; iterator->next = yr_process_get_next_memory_block; // In a process scan file size is undefined, when the file_size function is // set to NULL the value returned by the filesize keyword is YR_UNDEFINED. iterator->file_size = NULL; context->buffer = NULL; context->buffer_size = 0; context->current_block.base = 0; context->current_block.size = 0; context->current_block.context = context; context->current_block.fetch_data = yr_process_fetch_memory_block_data; context->proc_info = NULL; GOTO_EXIT_ON_ERROR_WITH_CLEANUP( _yr_process_attach(pid, context), yr_free(context)); result = ERROR_SUCCESS; _exit: YR_DEBUG_FPRINTF(2, stderr, "} = %d // %s()\n", result, __FUNCTION__); return result; } YR_API int yr_process_close_iterator(YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {}\n", __FUNCTION__); YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; if (context != NULL) { _yr_process_detach(context); if (context->buffer != NULL) yr_free((void*) context->buffer); yr_free(context->proc_info); yr_free(context); iterator->context = NULL; } return ERROR_SUCCESS; } yara-4.1.3/libyara/proc/000077500000000000000000000000001413423160300150365ustar00rootroot00000000000000yara-4.1.3/libyara/proc/freebsd.c000066400000000000000000000110271413423160300166150ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_FREEBSD_PROC) #include #include #include #include #include #include #include #include #include typedef struct _YR_PROC_INFO { int pid; struct ptrace_vm_entry vm_entry; } YR_PROC_INFO; int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { int status; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) yr_malloc(sizeof(YR_PROC_INFO)); if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; context->proc_info = proc_info; proc_info->pid = pid; if (ptrace(PT_ATTACH, pid, NULL, 0) == -1) { yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } status = 0; if (waitpid(pid, &status, 0) == -1) { ptrace(PT_DETACH, proc_info->pid, NULL, 0); yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } return ERROR_SUCCESS; } int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; ptrace(PT_DETACH, proc_info->pid, NULL, 0); proc_info->vm_entry.pve_path = NULL; return ERROR_SUCCESS; } YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; struct ptrace_io_desc io_desc; if (context->buffer_size < block->size) { if (context->buffer != NULL) yr_free((void*) context->buffer); context->buffer = (const uint8_t*) yr_malloc(block->size); if (context->buffer != NULL) { context->buffer_size = block->size; } else { context->buffer_size = 0; return NULL; } } io_desc.piod_op = PIOD_READ_D; io_desc.piod_offs = (void*) block->base; io_desc.piod_addr = (void*) context->buffer; io_desc.piod_len = block->size; if (ptrace(PT_IO, proc_info->pid, (char*) &io_desc, 0) == -1) { return NULL; } return context->buffer; } YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; char buf[4096]; proc_info->vm_entry.pve_path = buf; proc_info->vm_entry.pve_pathlen = sizeof(buf); iterator->last_error = ERROR_SUCCESS; if (ptrace(PT_VM_ENTRY, proc_info->pid, (char*) (&proc_info->vm_entry), 0) == -1) { return NULL; } context->current_block.base = proc_info->vm_entry.pve_start; context->current_block.size = proc_info->vm_entry.pve_end - proc_info->vm_entry.pve_start + 1; return &context->current_block; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; proc_info->vm_entry.pve_entry = 0; return yr_process_get_next_memory_block(iterator); } #endif yara-4.1.3/libyara/proc/linux.c000066400000000000000000000125231413423160300163440ustar00rootroot00000000000000/* Copyright (c) 2007-2021. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_LINUX_PROC) #include #include #include #include #include #include #include #include #include #include typedef struct _YR_PROC_INFO { int pid; int mem_fd; FILE* maps; } YR_PROC_INFO; int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { char buffer[256]; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) yr_malloc(sizeof(YR_PROC_INFO)); if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; context->proc_info = proc_info; proc_info->pid = pid; proc_info->maps = NULL; proc_info->mem_fd = -1; snprintf(buffer, sizeof(buffer), "/proc/%u/maps", pid); proc_info->maps = fopen(buffer, "r"); if (proc_info->maps == NULL) { yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } snprintf(buffer, sizeof(buffer), "/proc/%u/mem", pid); proc_info->mem_fd = open(buffer, O_RDONLY); if (proc_info->mem_fd == -1) { fclose(proc_info->maps); proc_info->maps = NULL; yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } return ERROR_SUCCESS; } int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; fclose(proc_info->maps); close(proc_info->mem_fd); return ERROR_SUCCESS; } YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { const uint8_t* result = NULL; YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; if (context->buffer_size < block->size) { if (context->buffer != NULL) yr_free((void*) context->buffer); context->buffer = (const uint8_t*) yr_malloc(block->size); if (context->buffer != NULL) { context->buffer_size = block->size; } else { context->buffer_size = 0; goto _exit; // return NULL; } } if (pread( proc_info->mem_fd, (void*) context->buffer, block->size, block->base) == -1) { goto _exit; // return NULL; } result = context->buffer; _exit:; YR_DEBUG_FPRINTF(2, stderr, "- %s() {} = %p\n", __FUNCTION__, result); return result; } YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_MEMORY_BLOCK* result = NULL; YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; char buffer[256]; uint64_t begin, end; if (fgets(buffer, sizeof(buffer), proc_info->maps) != NULL) { sscanf(buffer, "%" SCNx64 "-%" SCNx64, &begin, &end); context->current_block.base = begin; context->current_block.size = end - begin; result = &context->current_block; } // If we haven't read the whole line, skip over the rest. if (strrchr(buffer, '\n') == NULL) { int c; do { c = fgetc(proc_info->maps); } while (c >= 0 && c != '\n'); } iterator->last_error = ERROR_SUCCESS; YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = %p // .base=0x%" PRIx64 " .size=%" PRIu64 "\n", __FUNCTION__, result, context->current_block.base, context->current_block.size); return result; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_DEBUG_FPRINTF(2, stderr, "+ %s() {\n", __FUNCTION__); YR_MEMORY_BLOCK* result = NULL; YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; if (fseek(proc_info->maps, 0, SEEK_SET) != 0) { result = NULL; goto _exit; } result = yr_process_get_next_memory_block(iterator); _exit: YR_DEBUG_FPRINTF(2, stderr, "} = %p // %s()\n", result, __FUNCTION__); return result; } #endif yara-4.1.3/libyara/proc/mach.c000066400000000000000000000107771413423160300161260ustar00rootroot00000000000000/* Copyright (c) 2007-2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_MACH_PROC) #include #include #include #include #include #include #include typedef struct _YR_PROC_INFO { task_t task; } YR_PROC_INFO; int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) yr_malloc(sizeof(YR_PROC_INFO)); if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; kern_return_t kr = task_for_pid(mach_task_self(), pid, &proc_info->task); if (kr != KERN_SUCCESS) { yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } context->proc_info = proc_info; return ERROR_SUCCESS; } int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = context->proc_info; if (proc_info->task != MACH_PORT_NULL) mach_port_deallocate(mach_task_self(), proc_info->task); return ERROR_SUCCESS; } YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; YR_PROC_INFO* proc_info = context->proc_info; vm_size_t size = block->size; if (context->buffer_size < block->size) { if (context->buffer != NULL) yr_free((void*) context->buffer); context->buffer = (const uint8_t*) yr_malloc(block->size); if (context->buffer != NULL) { context->buffer_size = block->size; } else { context->buffer_size = 0; return NULL; } } if (vm_read_overwrite( proc_info->task, (vm_address_t) block->base, block->size, (vm_address_t) context->buffer, &size) != KERN_SUCCESS) { return NULL; } return context->buffer; } YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = context->proc_info; kern_return_t kr; mach_msg_type_number_t info_count; mach_port_t object; vm_region_basic_info_data_64_t info; vm_size_t size = 0; vm_address_t address = (vm_address_t) context->current_block.base + context->current_block.size; iterator->last_error = ERROR_SUCCESS; do { info_count = VM_REGION_BASIC_INFO_COUNT_64; kr = vm_region_64( proc_info->task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t) &info, &info_count, &object); if (kr == KERN_SUCCESS) { context->current_block.base = address; context->current_block.size = size; return &context->current_block; } } while (kr != KERN_INVALID_ADDRESS); return NULL; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; context->current_block.base = 0; context->current_block.size = 0; return yr_process_get_next_memory_block(iterator); } #endif yara-4.1.3/libyara/proc/none.c000066400000000000000000000041631413423160300161450ustar00rootroot00000000000000/* Copyright (c) 2007-2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_NO_PROC) #include #include int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { return ERROR_INVALID_ARGUMENT; } YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { return NULL; } YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { iterator->last_error = ERROR_SUCCESS; return NULL; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { return NULL; } #endif yara-4.1.3/libyara/proc/openbsd.c000066400000000000000000000116671413423160300166470ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_OPENBSD_PROC) #include #include #include #include #include #include #include #include typedef struct _YR_PROC_INFO { int pid; uint64_t old_end; struct kinfo_vmentry vm_entry; } YR_PROC_INFO; int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { int status; size_t len = sizeof(struct kinfo_vmentry); int mib[] = {CTL_KERN, KERN_PROC_VMMAP, pid}; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) yr_malloc(sizeof(YR_PROC_INFO)); if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; proc_info->pid = pid; if (ptrace(PT_ATTACH, pid, NULL, 0) == -1) { yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } status = 0; if (waitpid(pid, &status, 0) == -1) { ptrace(PT_DETACH, proc_info->pid, NULL, 0); yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } if (sysctl(mib, 3, &proc_info->vm_entry, &len, NULL, 0) < 0) { ptrace(PT_DETACH, proc_info->pid, NULL, 0); yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } context->proc_info = proc_info; return ERROR_SUCCESS; } int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; ptrace(PT_DETACH, proc_info->pid, NULL, 0); return ERROR_SUCCESS; } YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; struct ptrace_io_desc io_desc; if (context->buffer_size < block->size) { if (context->buffer != NULL) yr_free((void*) context->buffer); context->buffer = (const uint8_t*) yr_malloc(block->size); if (context->buffer != NULL) { context->buffer_size = block->size; } else { context->buffer_size = 0; return NULL; } } io_desc.piod_op = PIOD_READ_D; io_desc.piod_offs = (void*) block->base; io_desc.piod_addr = (void*) context->buffer; io_desc.piod_len = block->size; if (ptrace(PT_IO, proc_info->pid, (char*) &io_desc, 0) == -1) return NULL; return context->buffer; } YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; int mib[] = {CTL_KERN, KERN_PROC_VMMAP, proc_info->pid}; size_t len = sizeof(struct kinfo_vmentry); iterator->last_error = ERROR_SUCCESS; if (sysctl(mib, 3, &proc_info->vm_entry, &len, NULL, 0) < 0) return NULL; // no more blocks if (proc_info->old_end == proc_info->vm_entry.kve_end) return NULL; proc_info->old_end = proc_info->vm_entry.kve_end; context->current_block.base = proc_info->vm_entry.kve_start; context->current_block.size = proc_info->vm_entry.kve_end - proc_info->vm_entry.kve_start; proc_info->vm_entry.kve_start = proc_info->vm_entry.kve_start + 1; return &context->current_block; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; proc_info->vm_entry.kve_start = 0; return yr_process_get_next_memory_block(iterator); } #endif yara-4.1.3/libyara/proc/windows.c000066400000000000000000000122201413423160300166710ustar00rootroot00000000000000/* Copyright (c) 2007-2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(USE_WINDOWS_PROC) #include #include #include #include typedef struct _YR_PROC_INFO { HANDLE hProcess; SYSTEM_INFO si; } YR_PROC_INFO; int _yr_process_attach(int pid, YR_PROC_ITERATOR_CTX* context) { TOKEN_PRIVILEGES tokenPriv; LUID luidDebug; HANDLE hToken = NULL; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) yr_malloc(sizeof(YR_PROC_INFO)); if (proc_info == NULL) return ERROR_INSUFFICIENT_MEMORY; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) && LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidDebug)) { tokenPriv.PrivilegeCount = 1; tokenPriv.Privileges[0].Luid = luidDebug; tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges( hToken, FALSE, &tokenPriv, sizeof(tokenPriv), NULL, NULL); } if (hToken != NULL) CloseHandle(hToken); proc_info->hProcess = OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid); if (proc_info->hProcess == NULL) { yr_free(proc_info); return ERROR_COULD_NOT_ATTACH_TO_PROCESS; } GetSystemInfo(&proc_info->si); context->proc_info = proc_info; return ERROR_SUCCESS; } int _yr_process_detach(YR_PROC_ITERATOR_CTX* context) { YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; CloseHandle(proc_info->hProcess); return ERROR_SUCCESS; } YR_API const uint8_t* yr_process_fetch_memory_block_data(YR_MEMORY_BLOCK* block) { SIZE_T read; YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) block->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; if (context->buffer_size < block->size) { if (context->buffer != NULL) yr_free((void*) context->buffer); context->buffer = (const uint8_t*) yr_malloc(block->size); if (context->buffer != NULL) { context->buffer_size = block->size; } else { context->buffer_size = 0; return NULL; } } if (ReadProcessMemory( proc_info->hProcess, (LPCVOID) block->base, (LPVOID) context->buffer, (SIZE_T) block->size, &read) == FALSE) { return NULL; } return context->buffer; } YR_API YR_MEMORY_BLOCK* yr_process_get_next_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; MEMORY_BASIC_INFORMATION mbi; PVOID address = (PVOID)( context->current_block.base + context->current_block.size); iterator->last_error = ERROR_SUCCESS; while (address < proc_info->si.lpMaximumApplicationAddress && VirtualQueryEx(proc_info->hProcess, address, &mbi, sizeof(mbi)) != 0) { // mbi.RegionSize can overflow address while scanning a 64-bit process // with a 32-bit YARA. if ((uint8_t*) address + mbi.RegionSize <= (uint8_t*) address) break; if (mbi.State == MEM_COMMIT && ((mbi.Protect & PAGE_NOACCESS) == 0)) { context->current_block.base = (size_t) mbi.BaseAddress; context->current_block.size = mbi.RegionSize; return &context->current_block; } address = (uint8_t*) address + mbi.RegionSize; } return NULL; } YR_API YR_MEMORY_BLOCK* yr_process_get_first_memory_block( YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_PROC_ITERATOR_CTX* context = (YR_PROC_ITERATOR_CTX*) iterator->context; YR_PROC_INFO* proc_info = (YR_PROC_INFO*) context->proc_info; context->current_block.size = 0; context->current_block.base = (size_t) proc_info->si.lpMinimumApplicationAddress; return yr_process_get_next_memory_block(iterator); } #endif yara-4.1.3/libyara/re.c000066400000000000000000001654461413423160300146650ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This module implements a regular expressions engine based on Thompson's algorithm as described by Russ Cox in http://swtch.com/~rsc/regexp/regexp2.html. What the article names a "thread" has been named a "fiber" in this code, in order to avoid confusion with operating system threads. */ #include #include #include #include #include #include #include #include #include #include #include #include #define EMIT_BACKWARDS 0x01 #define EMIT_DONT_SET_FORWARDS_CODE 0x02 #define EMIT_DONT_SET_BACKWARDS_CODE 0x04 #ifndef INT16_MAX #define INT16_MAX (32767) #endif typedef uint8_t RE_SPLIT_ID_TYPE; typedef struct _RE_REPEAT_ARGS { uint16_t min; uint16_t max; int32_t offset; } RE_REPEAT_ARGS; typedef struct _RE_REPEAT_ANY_ARGS { uint16_t min; uint16_t max; } RE_REPEAT_ANY_ARGS; typedef struct _RE_EMIT_CONTEXT { YR_ARENA* arena; RE_SPLIT_ID_TYPE next_split_id; } RE_EMIT_CONTEXT; #define CHAR_IN_CLASS(cls, chr) ((cls)[(chr) / 8] & 1 << ((chr) % 8)) static bool _yr_re_is_char_in_class( RE_CLASS* re_class, uint8_t chr, int case_insensitive) { int result = CHAR_IN_CLASS(re_class->bitmap, chr); if (case_insensitive) result |= CHAR_IN_CLASS(re_class->bitmap, yr_altercase[chr]); if (re_class->negated) result = !result; return result; } static bool _yr_re_is_word_char(const uint8_t* input, uint8_t character_size) { int result = ((yr_isalnum(input) || (*input) == '_')); if (character_size == 2) result = result && (*(input + 1) == 0); return result; } RE_NODE* yr_re_node_create(int type) { RE_NODE* result = (RE_NODE*) yr_malloc(sizeof(RE_NODE)); if (result != NULL) { result->type = type; result->children_head = NULL; result->children_tail = NULL; result->prev_sibling = NULL; result->next_sibling = NULL; result->greedy = true; result->forward_code_ref = YR_ARENA_NULL_REF; result->backward_code_ref = YR_ARENA_NULL_REF; } return result; } void yr_re_node_destroy(RE_NODE* node) { RE_NODE* child = node->children_head; RE_NODE* next_child; while (child != NULL) { next_child = child->next_sibling; yr_re_node_destroy(child); child = next_child; } if (node->type == RE_NODE_CLASS) yr_free(node->re_class); yr_free(node); } //////////////////////////////////////////////////////////////////////////////// // Appends a node to the end of the children list. // void yr_re_node_append_child(RE_NODE* node, RE_NODE* child) { if (node->children_head == NULL) node->children_head = child; if (node->children_tail != NULL) node->children_tail->next_sibling = child; child->prev_sibling = node->children_tail; node->children_tail = child; } //////////////////////////////////////////////////////////////////////////////// // Appends a node to the beginning of the children list. // void yr_re_node_prepend_child(RE_NODE* node, RE_NODE* child) { child->next_sibling = node->children_head; if (node->children_head != NULL) node->children_head->prev_sibling = child; node->children_head = child; if (node->children_tail == NULL) node->children_tail = child; } int yr_re_ast_create(RE_AST** re_ast) { *re_ast = (RE_AST*) yr_malloc(sizeof(RE_AST)); if (*re_ast == NULL) return ERROR_INSUFFICIENT_MEMORY; (*re_ast)->flags = 0; (*re_ast)->root_node = NULL; return ERROR_SUCCESS; } void yr_re_ast_destroy(RE_AST* re_ast) { if (re_ast->root_node != NULL) yr_re_node_destroy(re_ast->root_node); yr_free(re_ast); } //////////////////////////////////////////////////////////////////////////////// // Parses a regexp but don't emit its code. A further call to // yr_re_ast_emit_code is required to get the code. // int yr_re_parse(const char* re_string, RE_AST** re_ast, RE_ERROR* error) { return yr_parse_re_string(re_string, re_ast, error); } //////////////////////////////////////////////////////////////////////////////// // Parses a hex string but don't emit its code. A further call to // yr_re_ast_emit_code is required to get the code. // int yr_re_parse_hex(const char* hex_string, RE_AST** re_ast, RE_ERROR* error) { return yr_parse_hex_string(hex_string, re_ast, error); } //////////////////////////////////////////////////////////////////////////////// // Parses the regexp and emit its code to the provided to the // YR_RE_CODE_SECTION in the specified arena. // int yr_re_compile( const char* re_string, int flags, YR_ARENA* arena, YR_ARENA_REF* ref, RE_ERROR* error) { RE_AST* re_ast; RE _re; FAIL_ON_ERROR(yr_re_parse(re_string, &re_ast, error)); _re.flags = flags; FAIL_ON_ERROR_WITH_CLEANUP( yr_arena_write_data(arena, YR_RE_CODE_SECTION, &_re, sizeof(_re), ref), yr_re_ast_destroy(re_ast)); FAIL_ON_ERROR_WITH_CLEANUP( yr_re_ast_emit_code(re_ast, arena, false), yr_re_ast_destroy(re_ast)); yr_re_ast_destroy(re_ast); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Verifies if the target string matches the pattern // // Args: // context: Scan context // re: A pointer to a compiled regexp // target: Target string // // Returns: // See return codes for yr_re_exec // int yr_re_match(YR_SCAN_CONTEXT* context, RE* re, const char* target) { int result; yr_re_exec( context, re->code, (uint8_t*) target, strlen(target), 0, re->flags | RE_FLAGS_SCAN, NULL, NULL, &result); return result; } //////////////////////////////////////////////////////////////////////////////// // Verifies if the provided regular expression is just a literal string // like "abc", "12345", without any wildcard, operator, etc. In that case // returns the string as a SIZED_STRING, or returns NULL if otherwise. // // The caller is responsible for deallocating the returned SIZED_STRING by // calling yr_free. // SIZED_STRING* yr_re_ast_extract_literal(RE_AST* re_ast) { SIZED_STRING* string; RE_NODE* child; int length = 0; if (re_ast->root_node->type == RE_NODE_LITERAL) { length = 1; } else if (re_ast->root_node->type == RE_NODE_CONCAT) { child = re_ast->root_node->children_tail; while (child != NULL && child->type == RE_NODE_LITERAL) { length++; child = child->prev_sibling; } if (child != NULL) return NULL; } else { return NULL; } string = (SIZED_STRING*) yr_malloc(sizeof(SIZED_STRING) + length); if (string == NULL) return NULL; string->length = length; string->flags = 0; if (re_ast->root_node->type == RE_NODE_LITERAL) { string->c_string[0] = re_ast->root_node->value; } else { child = re_ast->root_node->children_tail; while (child != NULL) { string->c_string[--length] = child->value; child = child->prev_sibling; } } string->c_string[string->length] = '\0'; return string; } int _yr_re_node_has_unbounded_quantifier_for_dot(RE_NODE* re_node) { RE_NODE* child; if ((re_node->type == RE_NODE_STAR || re_node->type == RE_NODE_PLUS) && re_node->children_head->type == RE_NODE_ANY) return true; if (re_node->type == RE_NODE_RANGE_ANY && re_node->end == RE_MAX_RANGE) return true; if (re_node->type == RE_NODE_CONCAT) { child = re_node->children_tail; while (child != NULL) { if (_yr_re_node_has_unbounded_quantifier_for_dot(child)) return true; child = child->prev_sibling; } } return false; } //////////////////////////////////////////////////////////////////////////////// // Detects the use of .*, .+ or .{x,} in a regexp. The use of wildcards with // quantifiers that don't have a reasonably small upper bound causes a // performance penalty. This function dectects such cases in order to warn the // user about this. // int yr_re_ast_has_unbounded_quantifier_for_dot(RE_AST* re_ast) { return _yr_re_node_has_unbounded_quantifier_for_dot(re_ast->root_node); } //////////////////////////////////////////////////////////////////////////////// // In some cases splitting a regular expression (or hex string) in two parts is // convenient for increasing performance. This happens when the pattern contains // a large gap (a.k.a jump), for example: { 01 02 03 [0-999] 04 05 06 } // In this case the string is splitted in { 01 02 03 } and { 04 05 06 } where // the latter is chained to the former. This means that { 01 02 03 } and // { 04 05 06 } are handled as individual strings, and when both of them are // found, YARA verifies if the distance between the matches complies with the // [0-999] restriction. // // This function traverses a regexp's AST looking for nodes where it should be // splitted. It must be noticed that this only applies to two-level ASTs (i.e. // an AST consisting in a RE_NODE_CONCAT at the root where all the children are // leaves). // // For example, { 01 02 03 [0-1000] 04 05 06 [500-2000] 07 08 09 } has the // following AST: // // RE_NODE_CONCAT // | // |- RE_NODE_LITERAL (01) // |- RE_NODE_LITERAL (02) // |- RE_NODE_LITERAL (03) // |- RE_NODE_RANGE_ANY (start=0, end=1000) // |- RE_NODE_LITERAL (04) // |- RE_NODE_LITERAL (05) // |- RE_NODE_LITERAL (06) // |- RE_NODE_RANGE_ANY (start=500, end=2000) // |- RE_NODE_LITERAL (07) // |- RE_NODE_LITERAL (08) // |- RE_NODE_LITERAL (09) // // If the AST above is passed in the re_ast argument, it will be trimmed to: // // RE_NODE_CONCAT // | // |- RE_NODE_LITERAL (01) // |- RE_NODE_LITERAL (02) // |- RE_NODE_LITERAL (03) // // While remainder_re_ast will be: // // RE_NODE_CONCAT // | // |- RE_NODE_LITERAL (04) // |- RE_NODE_LITERAL (05) // |- RE_NODE_LITERAL (06) // |- RE_NODE_RANGE_ANY (start=500, end=2000) // |- RE_NODE_LITERAL (07) // |- RE_NODE_LITERAL (08) // |- RE_NODE_LITERAL (09) // // The caller is responsible for freeing the new AST in remainder_re_ast by // calling yr_re_ast_destroy. // // The integers pointed to by min_gap and max_gap will be filled with the // minimum and maximum gap size between the sub-strings represented by the // two ASTs. // int yr_re_ast_split_at_chaining_point( RE_AST* re_ast, RE_AST** remainder_re_ast, int32_t* min_gap, int32_t* max_gap) { RE_NODE* child; RE_NODE* concat; int result; *remainder_re_ast = NULL; *min_gap = 0; *max_gap = 0; if (re_ast->root_node->type != RE_NODE_CONCAT) return ERROR_SUCCESS; child = re_ast->root_node->children_head; while (child != NULL) { if (!child->greedy && child->type == RE_NODE_RANGE_ANY && child->prev_sibling != NULL && child->next_sibling != NULL && (child->start > YR_STRING_CHAINING_THRESHOLD || child->end > YR_STRING_CHAINING_THRESHOLD)) { result = yr_re_ast_create(remainder_re_ast); if (result != ERROR_SUCCESS) return result; concat = yr_re_node_create(RE_NODE_CONCAT); if (concat == NULL) return ERROR_INSUFFICIENT_MEMORY; concat->children_head = child->next_sibling; concat->children_tail = re_ast->root_node->children_tail; re_ast->root_node->children_tail = child->prev_sibling; child->prev_sibling->next_sibling = NULL; child->next_sibling->prev_sibling = NULL; *min_gap = child->start; *max_gap = child->end; (*remainder_re_ast)->root_node = concat; (*remainder_re_ast)->flags = re_ast->flags; yr_re_node_destroy(child); return ERROR_SUCCESS; } child = child->next_sibling; } return ERROR_SUCCESS; } int _yr_emit_inst( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, YR_ARENA_REF* instruction_ref) { FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); return ERROR_SUCCESS; } int _yr_emit_inst_arg_uint8( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, uint8_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &argument, sizeof(uint8_t), argument_ref)); return ERROR_SUCCESS; } int _yr_emit_inst_arg_uint16( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, uint16_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &argument, sizeof(uint16_t), argument_ref)); return ERROR_SUCCESS; } int _yr_emit_inst_arg_uint32( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, uint32_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &argument, sizeof(uint32_t), argument_ref)); return ERROR_SUCCESS; } int _yr_emit_inst_arg_int16( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, int16_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &argument, sizeof(int16_t), argument_ref)); return ERROR_SUCCESS; } int _yr_emit_inst_arg_struct( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, void* structure, size_t structure_size, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, structure, structure_size, argument_ref)); return ERROR_SUCCESS; } int _yr_emit_split( RE_EMIT_CONTEXT* emit_context, uint8_t opcode, int16_t argument, YR_ARENA_REF* instruction_ref, YR_ARENA_REF* argument_ref) { assert(opcode == RE_OPCODE_SPLIT_A || opcode == RE_OPCODE_SPLIT_B); if (emit_context->next_split_id == RE_MAX_SPLIT_ID) return ERROR_REGULAR_EXPRESSION_TOO_COMPLEX; FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &opcode, sizeof(uint8_t), instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &emit_context->next_split_id, sizeof(RE_SPLIT_ID_TYPE), NULL)); emit_context->next_split_id++; FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, &argument, sizeof(int16_t), argument_ref)); return ERROR_SUCCESS; } #define current_re_code_offset() \ yr_arena_get_current_offset(emit_context->arena, YR_RE_CODE_SECTION) static int _yr_re_emit( RE_EMIT_CONTEXT* emit_context, RE_NODE* re_node, int flags, YR_ARENA_REF* code_ref) { yr_arena_off_t jmp_offset; yr_arena_off_t bookmark_1 = 0; yr_arena_off_t bookmark_2 = 0; yr_arena_off_t bookmark_3 = 0; yr_arena_off_t bookmark_4 = 0; bool emit_split; bool emit_repeat; bool emit_prolog; bool emit_epilog; RE_REPEAT_ARGS repeat_args; RE_REPEAT_ARGS* repeat_start_args_addr; RE_REPEAT_ANY_ARGS repeat_any_args; RE_NODE* child; int16_t* split_offset_addr = NULL; int16_t* jmp_offset_addr = NULL; YR_ARENA_REF instruction_ref = YR_ARENA_NULL_REF; YR_ARENA_REF split_offset_ref; YR_ARENA_REF jmp_instruction_ref; YR_ARENA_REF jmp_offset_ref; YR_ARENA_REF repeat_start_args_ref; switch (re_node->type) { case RE_NODE_LITERAL: FAIL_ON_ERROR(_yr_emit_inst_arg_uint8( emit_context, RE_OPCODE_LITERAL, re_node->value, &instruction_ref, NULL)); break; case RE_NODE_MASKED_LITERAL: FAIL_ON_ERROR(_yr_emit_inst_arg_uint16( emit_context, RE_OPCODE_MASKED_LITERAL, re_node->mask << 8 | re_node->value, &instruction_ref, NULL)); break; case RE_NODE_WORD_CHAR: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_WORD_CHAR, &instruction_ref)); break; case RE_NODE_NON_WORD_CHAR: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_NON_WORD_CHAR, &instruction_ref)); break; case RE_NODE_WORD_BOUNDARY: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_WORD_BOUNDARY, &instruction_ref)); break; case RE_NODE_NON_WORD_BOUNDARY: FAIL_ON_ERROR(_yr_emit_inst( emit_context, RE_OPCODE_NON_WORD_BOUNDARY, &instruction_ref)); break; case RE_NODE_SPACE: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_SPACE, &instruction_ref)); break; case RE_NODE_NON_SPACE: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_NON_SPACE, &instruction_ref)); break; case RE_NODE_DIGIT: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_DIGIT, &instruction_ref)); break; case RE_NODE_NON_DIGIT: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_NON_DIGIT, &instruction_ref)); break; case RE_NODE_ANY: FAIL_ON_ERROR(_yr_emit_inst(emit_context, RE_OPCODE_ANY, &instruction_ref)); break; case RE_NODE_CLASS: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_CLASS, &instruction_ref)); FAIL_ON_ERROR(yr_arena_write_data( emit_context->arena, YR_RE_CODE_SECTION, re_node->re_class, sizeof(*re_node->re_class), NULL)); break; case RE_NODE_ANCHOR_START: FAIL_ON_ERROR(_yr_emit_inst( emit_context, RE_OPCODE_MATCH_AT_START, &instruction_ref)); break; case RE_NODE_ANCHOR_END: FAIL_ON_ERROR( _yr_emit_inst(emit_context, RE_OPCODE_MATCH_AT_END, &instruction_ref)); break; case RE_NODE_CONCAT: FAIL_ON_ERROR(_yr_re_emit( emit_context, (flags & EMIT_BACKWARDS) ? re_node->children_tail : re_node->children_head, flags, &instruction_ref)); if (flags & EMIT_BACKWARDS) child = re_node->children_tail->prev_sibling; else child = re_node->children_head->next_sibling; while (child != NULL) { FAIL_ON_ERROR(_yr_re_emit(emit_context, child, flags, NULL)); child = (flags & EMIT_BACKWARDS) ? child->prev_sibling : child->next_sibling; } break; case RE_NODE_PLUS: // Code for e+ looks like: // // L1: code for e // split L1, L2 // L2: // FAIL_ON_ERROR(_yr_re_emit( emit_context, re_node->children_head, flags, &instruction_ref)); jmp_offset = instruction_ref.offset - current_re_code_offset(); if (jmp_offset < INT16_MIN) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; FAIL_ON_ERROR(_yr_emit_split( emit_context, re_node->greedy ? RE_OPCODE_SPLIT_B : RE_OPCODE_SPLIT_A, (int16_t) jmp_offset, NULL, NULL)); break; case RE_NODE_STAR: // Code for e* looks like: // // L1: split L1, L2 // code for e // jmp L1 // L2: FAIL_ON_ERROR(_yr_emit_split( emit_context, re_node->greedy ? RE_OPCODE_SPLIT_A : RE_OPCODE_SPLIT_B, 0, &instruction_ref, &split_offset_ref)); FAIL_ON_ERROR( _yr_re_emit(emit_context, re_node->children_head, flags, NULL)); jmp_offset = instruction_ref.offset - current_re_code_offset(); if (jmp_offset < INT16_MIN) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; // Emit jump with offset set to 0. FAIL_ON_ERROR(_yr_emit_inst_arg_int16( emit_context, RE_OPCODE_JUMP, (int16_t) jmp_offset, NULL, NULL)); jmp_offset = current_re_code_offset() - instruction_ref.offset; if (jmp_offset > INT16_MAX) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; // Update split offset. split_offset_addr = (int16_t*) yr_arena_ref_to_ptr( emit_context->arena, &split_offset_ref); *split_offset_addr = (int16_t) jmp_offset; break; case RE_NODE_ALT: // Code for e1|e2 looks like: // // split L1, L2 // L1: code for e1 // jmp L3 // L2: code for e2 // L3: // Emit a split instruction with offset set to 0 temporarily. Offset // will be updated after we know the size of the code generated for // the left node (e1). FAIL_ON_ERROR(_yr_emit_split( emit_context, RE_OPCODE_SPLIT_A, 0, &instruction_ref, &split_offset_ref)); FAIL_ON_ERROR( _yr_re_emit(emit_context, re_node->children_head, flags, NULL)); // Emit jump with offset set to 0. FAIL_ON_ERROR(_yr_emit_inst_arg_int16( emit_context, RE_OPCODE_JUMP, 0, &jmp_instruction_ref, &jmp_offset_ref)); jmp_offset = current_re_code_offset() - instruction_ref.offset; if (jmp_offset > INT16_MAX) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; // Update split offset. split_offset_addr = (int16_t*) yr_arena_ref_to_ptr( emit_context->arena, &split_offset_ref); *split_offset_addr = (int16_t) jmp_offset; FAIL_ON_ERROR( _yr_re_emit(emit_context, re_node->children_tail, flags, NULL)); jmp_offset = current_re_code_offset() - jmp_instruction_ref.offset; if (jmp_offset > INT16_MAX) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; // Update offset for jmp instruction. jmp_offset_addr = (int16_t*) yr_arena_ref_to_ptr( emit_context->arena, &jmp_offset_ref); *jmp_offset_addr = (int16_t) jmp_offset; break; case RE_NODE_RANGE_ANY: repeat_any_args.min = re_node->start; repeat_any_args.max = re_node->end; FAIL_ON_ERROR(_yr_emit_inst_arg_struct( emit_context, re_node->greedy ? RE_OPCODE_REPEAT_ANY_GREEDY : RE_OPCODE_REPEAT_ANY_UNGREEDY, &repeat_any_args, sizeof(repeat_any_args), &instruction_ref, NULL)); break; case RE_NODE_RANGE: // Code for e{n,m} looks like: // // code for e --- prolog // repeat_start n, m, L1 --+ // L0: code for e | repeat // repeat_end n, m, L0 --+ // L1: split L2, L3 --- split // L2: code for e --- epilog // L3: // // Not all sections (prolog, repeat, split and epilog) are generated in all // cases, it depends on the values of n and m. The following table shows // which sections are generated for the first few values of n and m. // // n,m prolog repeat split epilog // (min,max) // --------------------------------------- // 0,0 - - - - // 0,1 - - X X // 0,2 - 0,1 X X // 0,3 - 0,2 X X // 0,M - 0,M-1 X X // // 1,1 X - - - // 1,2 X - X X // 1,3 X 0,1 X X // 1,4 X 1,2 X X // 1,M X 1,M-2 X X // // 2,2 X - - X // 2,3 X 1,1 X X // 2,4 X 1,2 X X // 2,M X 1,M-2 X X // // 3,3 X 1,1 - X // 3,4 X 2,2 X X // 3,M X 2,M-2 X X // // 4,4 X 2,2 - X // 4,5 X 3,3 X X // 4,M X 3,M-2 X X // // The code can't consists simply in the repeat section, the prolog and // epilog are required because we can't have atoms pointing to code inside // the repeat loop. Atoms' forwards_code will point to code in the prolog // and backwards_code will point to code in the epilog (or in prolog if // epilog wasn't generated, like in n=1,m=1) emit_prolog = re_node->start > 0; emit_repeat = re_node->end > re_node->start + 1 || re_node->end > 2; emit_split = re_node->end > re_node->start; emit_epilog = re_node->end > re_node->start || re_node->end > 1; if (emit_prolog) { FAIL_ON_ERROR(_yr_re_emit( emit_context, re_node->children_head, flags, &instruction_ref)); } if (emit_repeat) { repeat_args.min = re_node->start; repeat_args.max = re_node->end; if (emit_prolog) { repeat_args.max--; repeat_args.min--; } if (emit_split) { repeat_args.max--; } else { repeat_args.min--; repeat_args.max--; } repeat_args.offset = 0; bookmark_1 = current_re_code_offset(); FAIL_ON_ERROR(_yr_emit_inst_arg_struct( emit_context, re_node->greedy ? RE_OPCODE_REPEAT_START_GREEDY : RE_OPCODE_REPEAT_START_UNGREEDY, &repeat_args, sizeof(repeat_args), emit_prolog ? NULL : &instruction_ref, &repeat_start_args_ref)); bookmark_2 = current_re_code_offset(); FAIL_ON_ERROR(_yr_re_emit( emit_context, re_node->children_head, flags | EMIT_DONT_SET_FORWARDS_CODE | EMIT_DONT_SET_BACKWARDS_CODE, NULL)); bookmark_3 = current_re_code_offset(); if (bookmark_2 - bookmark_3 < INT32_MIN) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; repeat_args.offset = (int32_t)(bookmark_2 - bookmark_3); FAIL_ON_ERROR(_yr_emit_inst_arg_struct( emit_context, re_node->greedy ? RE_OPCODE_REPEAT_END_GREEDY : RE_OPCODE_REPEAT_END_UNGREEDY, &repeat_args, sizeof(repeat_args), NULL, NULL)); bookmark_4 = current_re_code_offset(); repeat_start_args_addr = (RE_REPEAT_ARGS*) yr_arena_ref_to_ptr( emit_context->arena, &repeat_start_args_ref); if (bookmark_4 - bookmark_1 > INT32_MAX) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; repeat_start_args_addr->offset = (int32_t)(bookmark_4 - bookmark_1); } if (emit_split) { bookmark_1 = current_re_code_offset(); FAIL_ON_ERROR(_yr_emit_split( emit_context, re_node->greedy ? RE_OPCODE_SPLIT_A : RE_OPCODE_SPLIT_B, 0, NULL, &split_offset_ref)); } if (emit_epilog) { FAIL_ON_ERROR(_yr_re_emit( emit_context, re_node->children_head, emit_prolog ? flags | EMIT_DONT_SET_FORWARDS_CODE : flags, emit_prolog || emit_repeat ? NULL : &instruction_ref)); } if (emit_split) { bookmark_2 = current_re_code_offset(); if (bookmark_2 - bookmark_1 > INT16_MAX) return ERROR_REGULAR_EXPRESSION_TOO_LARGE; split_offset_addr = (int16_t*) yr_arena_ref_to_ptr( emit_context->arena, &split_offset_ref); *split_offset_addr = (int16_t)(bookmark_2 - bookmark_1); } break; } if (flags & EMIT_BACKWARDS) { if (!(flags & EMIT_DONT_SET_BACKWARDS_CODE)) { re_node->backward_code_ref.buffer_id = YR_RE_CODE_SECTION; re_node->backward_code_ref.offset = yr_arena_get_current_offset( emit_context->arena, YR_RE_CODE_SECTION); } } else { if (!(flags & EMIT_DONT_SET_FORWARDS_CODE)) { re_node->forward_code_ref = instruction_ref; } } if (code_ref != NULL) *code_ref = instruction_ref; return ERROR_SUCCESS; } int yr_re_ast_emit_code(RE_AST* re_ast, YR_ARENA* arena, int backwards_code) { RE_EMIT_CONTEXT emit_context; // Emit code for matching the regular expressions forwards. emit_context.arena = arena; emit_context.next_split_id = 0; FAIL_ON_ERROR(_yr_re_emit( &emit_context, re_ast->root_node, backwards_code ? EMIT_BACKWARDS : 0, NULL)); FAIL_ON_ERROR(_yr_emit_inst(&emit_context, RE_OPCODE_MATCH, NULL)); return ERROR_SUCCESS; } static int _yr_re_fiber_create(RE_FIBER_POOL* fiber_pool, RE_FIBER** new_fiber) { RE_FIBER* fiber; if (fiber_pool->fibers.head != NULL) { fiber = fiber_pool->fibers.head; fiber_pool->fibers.head = fiber->next; if (fiber_pool->fibers.tail == fiber) fiber_pool->fibers.tail = NULL; } else { if (fiber_pool->fiber_count == RE_MAX_FIBERS) return ERROR_TOO_MANY_RE_FIBERS; fiber = (RE_FIBER*) yr_malloc(sizeof(RE_FIBER)); if (fiber == NULL) return ERROR_INSUFFICIENT_MEMORY; fiber_pool->fiber_count++; } fiber->ip = NULL; fiber->sp = -1; fiber->rc = -1; fiber->next = NULL; fiber->prev = NULL; *new_fiber = fiber; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Appends 'fiber' to 'fiber_list' // static void _yr_re_fiber_append(RE_FIBER_LIST* fiber_list, RE_FIBER* fiber) { assert(fiber->prev == NULL); assert(fiber->next == NULL); fiber->prev = fiber_list->tail; if (fiber_list->tail != NULL) fiber_list->tail->next = fiber; fiber_list->tail = fiber; if (fiber_list->head == NULL) fiber_list->head = fiber; assert(fiber_list->tail->next == NULL); assert(fiber_list->head->prev == NULL); } //////////////////////////////////////////////////////////////////////////////// // Verifies if a fiber with the same properties (ip, rc, sp, and stack values) // than 'target_fiber' exists in 'fiber_list'. The list is iterated from // the start until 'last_fiber' (inclusive). Fibers past 'last_fiber' are not // taken into account. // static int _yr_re_fiber_exists( RE_FIBER_LIST* fiber_list, RE_FIBER* target_fiber, RE_FIBER* last_fiber) { RE_FIBER* fiber = fiber_list->head; int equal_stacks; int i; if (last_fiber == NULL) return false; while (fiber != last_fiber->next) { if (fiber->ip == target_fiber->ip && fiber->sp == target_fiber->sp && fiber->rc == target_fiber->rc) { equal_stacks = true; for (i = 0; i <= fiber->sp; i++) { if (fiber->stack[i] != target_fiber->stack[i]) { equal_stacks = false; break; } } if (equal_stacks) return true; } fiber = fiber->next; } return false; } //////////////////////////////////////////////////////////////////////////////// // Clones a fiber in fiber_list and inserts the cloned fiber just after. // the original one. If fiber_list is: // // f1 -> f2 -> f3 -> f4 // // Splitting f2 will result in: // // f1 -> f2 -> cloned f2 -> f3 -> f4 // static int _yr_re_fiber_split( RE_FIBER_LIST* fiber_list, RE_FIBER_POOL* fiber_pool, RE_FIBER* fiber, RE_FIBER** new_fiber) { int32_t i; FAIL_ON_ERROR(_yr_re_fiber_create(fiber_pool, new_fiber)); (*new_fiber)->sp = fiber->sp; (*new_fiber)->ip = fiber->ip; (*new_fiber)->rc = fiber->rc; for (i = 0; i <= fiber->sp; i++) (*new_fiber)->stack[i] = fiber->stack[i]; (*new_fiber)->next = fiber->next; (*new_fiber)->prev = fiber; if (fiber->next != NULL) fiber->next->prev = *new_fiber; fiber->next = *new_fiber; if (fiber_list->tail == fiber) fiber_list->tail = *new_fiber; assert(fiber_list->tail->next == NULL); assert(fiber_list->head->prev == NULL); return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Kills a given fiber by removing it from the fiber list and putting it in the // fiber pool. // static RE_FIBER* _yr_re_fiber_kill( RE_FIBER_LIST* fiber_list, RE_FIBER_POOL* fiber_pool, RE_FIBER* fiber) { RE_FIBER* next_fiber = fiber->next; if (fiber->prev != NULL) fiber->prev->next = next_fiber; if (next_fiber != NULL) next_fiber->prev = fiber->prev; if (fiber_pool->fibers.tail != NULL) fiber_pool->fibers.tail->next = fiber; if (fiber_list->tail == fiber) fiber_list->tail = fiber->prev; if (fiber_list->head == fiber) fiber_list->head = next_fiber; fiber->next = NULL; fiber->prev = fiber_pool->fibers.tail; fiber_pool->fibers.tail = fiber; if (fiber_pool->fibers.head == NULL) fiber_pool->fibers.head = fiber; return next_fiber; } //////////////////////////////////////////////////////////////////////////////// // Kills all fibers from the given one up to the end of the fiber list. // static void _yr_re_fiber_kill_tail( RE_FIBER_LIST* fiber_list, RE_FIBER_POOL* fiber_pool, RE_FIBER* fiber) { RE_FIBER* prev_fiber = fiber->prev; if (prev_fiber != NULL) prev_fiber->next = NULL; fiber->prev = fiber_pool->fibers.tail; if (fiber_pool->fibers.tail != NULL) fiber_pool->fibers.tail->next = fiber; fiber_pool->fibers.tail = fiber_list->tail; fiber_list->tail = prev_fiber; if (fiber_list->head == fiber) fiber_list->head = NULL; if (fiber_pool->fibers.head == NULL) fiber_pool->fibers.head = fiber; } //////////////////////////////////////////////////////////////////////////////// // Kills all fibers in the fiber list. // static void _yr_re_fiber_kill_all( RE_FIBER_LIST* fiber_list, RE_FIBER_POOL* fiber_pool) { if (fiber_list->head != NULL) _yr_re_fiber_kill_tail(fiber_list, fiber_pool, fiber_list->head); } //////////////////////////////////////////////////////////////////////////////// // Executes a fiber until reaching an "matching" instruction. A "matching" // instruction is one that actually reads a byte from the input and performs // some matching. If the fiber reaches a split instruction, the new fiber is // also synced. // static int _yr_re_fiber_sync( RE_FIBER_LIST* fiber_list, RE_FIBER_POOL* fiber_pool, RE_FIBER* fiber_to_sync) { // A array for keeping track of which split instructions has been already // executed. Each split instruction within a regexp has an associated ID // between 0 and RE_MAX_SPLIT_ID-1. Keeping track of executed splits is // required to avoid infinite loops in regexps like (a*)* or (a|)* RE_SPLIT_ID_TYPE splits_executed[RE_MAX_SPLIT_ID]; RE_SPLIT_ID_TYPE splits_executed_count = 0; RE_SPLIT_ID_TYPE split_id, splits_executed_idx; int split_already_executed; RE_REPEAT_ARGS* repeat_args; RE_REPEAT_ANY_ARGS* repeat_any_args; RE_FIBER* fiber; RE_FIBER* last; RE_FIBER* next; RE_FIBER* branch_a; RE_FIBER* branch_b; fiber = fiber_to_sync; last = fiber_to_sync->next; while (fiber != last) { uint8_t opcode = *fiber->ip; switch (opcode) { case RE_OPCODE_SPLIT_A: case RE_OPCODE_SPLIT_B: split_id = *(RE_SPLIT_ID_TYPE*) (fiber->ip + 1); split_already_executed = false; for (splits_executed_idx = 0; splits_executed_idx < splits_executed_count; splits_executed_idx++) { if (split_id == splits_executed[splits_executed_idx]) { split_already_executed = true; break; } } if (split_already_executed) { fiber = _yr_re_fiber_kill(fiber_list, fiber_pool, fiber); } else { branch_a = fiber; FAIL_ON_ERROR( _yr_re_fiber_split(fiber_list, fiber_pool, branch_a, &branch_b)); // With RE_OPCODE_SPLIT_A the current fiber continues at the next // instruction in the stream (branch A), while the newly created // fiber starts at the address indicated by the instruction (branch B) // RE_OPCODE_SPLIT_B has the opposite behavior. if (opcode == RE_OPCODE_SPLIT_B) yr_swap(branch_a, branch_b, RE_FIBER*); // Branch A continues at the next instruction branch_a->ip += (sizeof(RE_SPLIT_ID_TYPE) + 3); // Branch B adds the offset encoded in the opcode to its instruction // pointer. branch_b->ip += *(int16_t*)( branch_b->ip + 1 // opcode size + sizeof(RE_SPLIT_ID_TYPE)); #ifdef YR_PARANOID_MODE // In normal conditions this should never happen. But with compiled // rules that has been hand-crafted by a malicious actor this could // happen. if (splits_executed_count >= RE_MAX_SPLIT_ID) return ERROR_INTERNAL_FATAL_ERROR; #endif splits_executed[splits_executed_count] = split_id; splits_executed_count++; } break; case RE_OPCODE_REPEAT_START_GREEDY: case RE_OPCODE_REPEAT_START_UNGREEDY: repeat_args = (RE_REPEAT_ARGS*) (fiber->ip + 1); assert(repeat_args->max > 0); branch_a = fiber; if (repeat_args->min == 0) { FAIL_ON_ERROR( _yr_re_fiber_split(fiber_list, fiber_pool, branch_a, &branch_b)); if (opcode == RE_OPCODE_REPEAT_START_UNGREEDY) yr_swap(branch_a, branch_b, RE_FIBER*); branch_b->ip += repeat_args->offset; } branch_a->stack[++branch_a->sp] = 0; branch_a->ip += (1 + sizeof(RE_REPEAT_ARGS)); break; case RE_OPCODE_REPEAT_END_GREEDY: case RE_OPCODE_REPEAT_END_UNGREEDY: repeat_args = (RE_REPEAT_ARGS*) (fiber->ip + 1); fiber->stack[fiber->sp]++; if (fiber->stack[fiber->sp] < repeat_args->min) { fiber->ip += repeat_args->offset; break; } branch_a = fiber; if (fiber->stack[fiber->sp] < repeat_args->max) { FAIL_ON_ERROR( _yr_re_fiber_split(fiber_list, fiber_pool, branch_a, &branch_b)); if (opcode == RE_OPCODE_REPEAT_END_GREEDY) yr_swap(branch_a, branch_b, RE_FIBER*); branch_b->ip += repeat_args->offset; } branch_a->sp--; branch_a->ip += (1 + sizeof(RE_REPEAT_ARGS)); break; case RE_OPCODE_REPEAT_ANY_GREEDY: case RE_OPCODE_REPEAT_ANY_UNGREEDY: repeat_any_args = (RE_REPEAT_ANY_ARGS*) (fiber->ip + 1); // If repetition counter (rc) is -1 it means that we are reaching this // instruction from the previous one in the instructions stream. In // this case let's initialize the counter to 0 and start looping. if (fiber->rc == -1) fiber->rc = 0; if (fiber->rc < repeat_any_args->min) { // Increase repetition counter and continue with next fiber. The // instruction pointer for this fiber is not incremented yet, this // fiber spins in this same instruction until reaching the minimum // number of repetitions. fiber->rc++; fiber = fiber->next; } else if (fiber->rc < repeat_any_args->max) { // Once the minimum number of repetitions are matched one fiber // remains spinning in this instruction until reaching the maximum // number of repetitions while new fibers are created. New fibers // start executing at the next instruction. next = fiber->next; branch_a = fiber; FAIL_ON_ERROR( _yr_re_fiber_split(fiber_list, fiber_pool, branch_a, &branch_b)); if (opcode == RE_OPCODE_REPEAT_ANY_UNGREEDY) yr_swap(branch_a, branch_b, RE_FIBER*); branch_a->rc++; branch_b->ip += (1 + sizeof(RE_REPEAT_ANY_ARGS)); branch_b->rc = -1; FAIL_ON_ERROR(_yr_re_fiber_sync(fiber_list, fiber_pool, branch_b)); fiber = next; } else { // When the maximum number of repetitions is reached the fiber keeps // executing at the next instruction. The repetition counter is set // to -1 indicating that we are not spinning in a repeat instruction // anymore. fiber->ip += (1 + sizeof(RE_REPEAT_ANY_ARGS)); fiber->rc = -1; } break; case RE_OPCODE_JUMP: fiber->ip += *(int16_t*) (fiber->ip + 1); break; default: fiber = fiber->next; } } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Executes a regular expression. The specified regular expression will try to // match the data starting at the address specified by "input". The "input" // pointer can point to any address inside a memory buffer. Arguments // "input_forwards_size" and "input_backwards_size" indicate how many bytes // can be accessible starting at "input" and going forwards and backwards // respectively. // // <--- input_backwards_size -->|<----------- input_forwards_size --------> // |-------- memory buffer -----------------------------------------------| // ^ // input // // Args: // YR_SCAN_CONTEXT *context - Scan context. // const uint8_t* code - Regexp code be executed // const uint8_t* input - Pointer to input data // size_t input_forwards_size - Number of accessible bytes starting at // "input" and going forwards. // size_t input_backwards_size - Number of accessible bytes starting at // "input" and going backwards // int flags - Flags: // RE_FLAGS_SCAN // RE_FLAGS_BACKWARDS // RE_FLAGS_EXHAUSTIVE // RE_FLAGS_WIDE // RE_FLAGS_NO_CASE // RE_FLAGS_DOT_ALL // RE_MATCH_CALLBACK_FUNC callback - Callback function // void* callback_args - Callback argument // int* matches - Pointer to an integer receiving the // number of matching bytes. Notice that // 0 means a zero-length match, while no // matches is -1. // Returns: // ERROR_SUCCESS or any other error code. int yr_re_exec( YR_SCAN_CONTEXT* context, const uint8_t* code, const uint8_t* input_data, size_t input_forwards_size, size_t input_backwards_size, int flags, RE_MATCH_CALLBACK_FUNC callback, void* callback_args, int* matches) { const uint8_t* input; const uint8_t* ip; uint8_t mask; uint8_t value; uint8_t character_size; RE_FIBER_LIST fibers; RE_FIBER* fiber; RE_FIBER* next_fiber; int bytes_matched; int max_bytes_matched; int match; int input_incr; int kill; int action; #define ACTION_NONE 0 #define ACTION_CONTINUE 1 #define ACTION_KILL 2 #define ACTION_KILL_TAIL 3 #define prolog \ { \ if ((bytes_matched >= max_bytes_matched) || \ (character_size == 2 && *(input + 1) != 0)) \ { \ action = ACTION_KILL; \ break; \ } \ } if (matches != NULL) *matches = -1; if (flags & RE_FLAGS_WIDE) character_size = 2; else character_size = 1; input = input_data; input_incr = character_size; if (flags & RE_FLAGS_BACKWARDS) { max_bytes_matched = (int) yr_min(input_backwards_size, YR_RE_SCAN_LIMIT); input -= character_size; input_incr = -input_incr; } else { max_bytes_matched = (int) yr_min(input_forwards_size, YR_RE_SCAN_LIMIT); } // Round down max_bytes_matched to a multiple of character_size, this way if // character_size is 2 and max_bytes_matched is odd we are ignoring the // extra byte which can't match anyways. max_bytes_matched = max_bytes_matched - max_bytes_matched % character_size; bytes_matched = 0; FAIL_ON_ERROR(_yr_re_fiber_create(&context->re_fiber_pool, &fiber)); fiber->ip = code; fibers.head = fiber; fibers.tail = fiber; FAIL_ON_ERROR_WITH_CLEANUP( _yr_re_fiber_sync(&fibers, &context->re_fiber_pool, fiber), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); while (fibers.head != NULL) { fiber = fibers.head; while (fiber != NULL) { next_fiber = fiber->next; if (_yr_re_fiber_exists(&fibers, fiber, fiber->prev)) _yr_re_fiber_kill(&fibers, &context->re_fiber_pool, fiber); fiber = next_fiber; } fiber = fibers.head; while (fiber != NULL) { ip = fiber->ip; action = ACTION_NONE; switch (*ip) { case RE_OPCODE_ANY: prolog; match = (flags & RE_FLAGS_DOT_ALL) || (*input != 0x0A); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_REPEAT_ANY_GREEDY: case RE_OPCODE_REPEAT_ANY_UNGREEDY: prolog; match = (flags & RE_FLAGS_DOT_ALL) || (*input != 0x0A); action = match ? ACTION_NONE : ACTION_KILL; // The instruction pointer is not incremented here. The current fiber // spins in this instruction until reaching the required number of // repetitions. The code controlling the number of repetitions is in // _yr_re_fiber_sync. break; case RE_OPCODE_LITERAL: prolog; if (flags & RE_FLAGS_NO_CASE) match = yr_lowercase[*input] == yr_lowercase[*(ip + 1)]; else match = (*input == *(ip + 1)); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 2; break; case RE_OPCODE_MASKED_LITERAL: prolog; value = *(int16_t*) (ip + 1) & 0xFF; mask = *(int16_t*) (ip + 1) >> 8; // We don't need to take into account the case-insensitive // case because this opcode is only used with hex strings, // which can't be case-insensitive. match = ((*input & mask) == value); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 3; break; case RE_OPCODE_CLASS: prolog; match = _yr_re_is_char_in_class( (RE_CLASS*) (ip + 1), *input, flags & RE_FLAGS_NO_CASE); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += (sizeof(RE_CLASS) + 1); break; case RE_OPCODE_WORD_CHAR: prolog; match = _yr_re_is_word_char(input, character_size); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_NON_WORD_CHAR: prolog; match = !_yr_re_is_word_char(input, character_size); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_SPACE: case RE_OPCODE_NON_SPACE: prolog; switch (*input) { case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': match = true; break; default: match = false; } if (*ip == RE_OPCODE_NON_SPACE) match = !match; action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_DIGIT: prolog; match = isdigit(*input); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_NON_DIGIT: prolog; match = !isdigit(*input); action = match ? ACTION_NONE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_WORD_BOUNDARY: case RE_OPCODE_NON_WORD_BOUNDARY: if (bytes_matched == 0 && input_backwards_size < character_size) { match = true; } else if (bytes_matched >= max_bytes_matched) { match = true; } else { assert(input < input_data + input_forwards_size); assert(input >= input_data - input_backwards_size); assert(input - input_incr < input_data + input_forwards_size); assert(input - input_incr >= input_data - input_backwards_size); match = _yr_re_is_word_char(input, character_size) != _yr_re_is_word_char(input - input_incr, character_size); } if (*ip == RE_OPCODE_NON_WORD_BOUNDARY) match = !match; action = match ? ACTION_CONTINUE : ACTION_KILL; fiber->ip += 1; break; case RE_OPCODE_MATCH_AT_START: if (flags & RE_FLAGS_BACKWARDS) kill = input_backwards_size > (size_t) bytes_matched; else kill = input_backwards_size > 0 || (bytes_matched != 0); action = kill ? ACTION_KILL : ACTION_CONTINUE; fiber->ip += 1; break; case RE_OPCODE_MATCH_AT_END: kill = flags & RE_FLAGS_BACKWARDS || input_forwards_size > (size_t) bytes_matched; action = kill ? ACTION_KILL : ACTION_CONTINUE; fiber->ip += 1; break; case RE_OPCODE_MATCH: if (matches != NULL) *matches = bytes_matched; if (flags & RE_FLAGS_EXHAUSTIVE) { if (callback != NULL) { if (flags & RE_FLAGS_BACKWARDS) { FAIL_ON_ERROR_WITH_CLEANUP( callback( input + character_size, bytes_matched, flags, callback_args), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); } else { FAIL_ON_ERROR_WITH_CLEANUP( callback(input_data, bytes_matched, flags, callback_args), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); } } action = ACTION_KILL; } else { action = ACTION_KILL_TAIL; } break; default: assert(false); } switch (action) { case ACTION_KILL: fiber = _yr_re_fiber_kill(&fibers, &context->re_fiber_pool, fiber); break; case ACTION_KILL_TAIL: _yr_re_fiber_kill_tail(&fibers, &context->re_fiber_pool, fiber); fiber = NULL; break; case ACTION_CONTINUE: FAIL_ON_ERROR_WITH_CLEANUP( _yr_re_fiber_sync(&fibers, &context->re_fiber_pool, fiber), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); break; default: next_fiber = fiber->next; FAIL_ON_ERROR_WITH_CLEANUP( _yr_re_fiber_sync(&fibers, &context->re_fiber_pool, fiber), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); fiber = next_fiber; } } input += input_incr; bytes_matched += character_size; if (flags & RE_FLAGS_SCAN && bytes_matched < max_bytes_matched) { FAIL_ON_ERROR_WITH_CLEANUP( _yr_re_fiber_create(&context->re_fiber_pool, &fiber), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); fiber->ip = code; _yr_re_fiber_append(&fibers, fiber); FAIL_ON_ERROR_WITH_CLEANUP( _yr_re_fiber_sync(&fibers, &context->re_fiber_pool, fiber), _yr_re_fiber_kill_all(&fibers, &context->re_fiber_pool)); } } return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // This function replaces yr_re_exec for regular expressions marked with flag // RE_FLAGS_FAST_REGEXP. These are regular expression whose code contain only // the following operations: RE_OPCODE_LITERAL, RE_OPCODE_MASKED_LITERAL, // RE_OPCODE_ANY, RE_OPCODE_REPEAT_ANY_UNGREEDY and RE_OPCODE_MATCH. Some // examples of regular expressions that can be executed with this function are: // // /foobar/ // /foo.*?bar/ // int yr_re_fast_exec( YR_SCAN_CONTEXT* context, const uint8_t* code, const uint8_t* input_data, size_t input_forwards_size, size_t input_backwards_size, int flags, RE_MATCH_CALLBACK_FUNC callback, void* callback_args, int* matches) { RE_REPEAT_ANY_ARGS* repeat_any_args; const uint8_t* code_stack[YR_MAX_FAST_RE_STACK]; const uint8_t* input_stack[YR_MAX_FAST_RE_STACK]; int matches_stack[YR_MAX_FAST_RE_STACK]; const uint8_t* input = input_data; const uint8_t* next_input; const uint8_t* ip = code; const uint8_t* next_opcode; uint8_t mask; uint8_t value; int stop; int input_incr; int sp = 0; int bytes_matched; int max_bytes_matched; max_bytes_matched = flags & RE_FLAGS_BACKWARDS ? (int) input_backwards_size : (int) input_forwards_size; input_incr = flags & RE_FLAGS_BACKWARDS ? -1 : 1; if (flags & RE_FLAGS_BACKWARDS) input--; code_stack[sp] = code; input_stack[sp] = input; matches_stack[sp] = 0; sp++; while (sp > 0) { sp--; ip = code_stack[sp]; input = input_stack[sp]; bytes_matched = matches_stack[sp]; stop = false; while (!stop) { if (*ip == RE_OPCODE_MATCH) { if (flags & RE_FLAGS_EXHAUSTIVE) { FAIL_ON_ERROR(callback( // When matching forwards the matching data always starts at // input_data, when matching backwards it starts at input + 1 or // input_data - input_backwards_size if input + 1 is outside the // buffer. flags & RE_FLAGS_BACKWARDS ? yr_max(input + 1, input_data - input_backwards_size) : input_data, // The number of matched bytes should not be larger than // max_bytes_matched. yr_min(bytes_matched, max_bytes_matched), flags, callback_args)); break; } else { if (matches != NULL) *matches = bytes_matched; return ERROR_SUCCESS; } } if (bytes_matched >= max_bytes_matched) break; switch (*ip) { case RE_OPCODE_LITERAL: if (*input == *(ip + 1)) { bytes_matched++; input += input_incr; ip += 2; } else { stop = true; } break; case RE_OPCODE_MASKED_LITERAL: value = *(int16_t*) (ip + 1) & 0xFF; mask = *(int16_t*) (ip + 1) >> 8; if ((*input & mask) == value) { bytes_matched++; input += input_incr; ip += 3; } else { stop = true; } break; case RE_OPCODE_ANY: bytes_matched++; input += input_incr; ip += 1; break; case RE_OPCODE_REPEAT_ANY_UNGREEDY: repeat_any_args = (RE_REPEAT_ANY_ARGS*) (ip + 1); next_opcode = ip + 1 + sizeof(RE_REPEAT_ANY_ARGS); for (int i = repeat_any_args->min + 1; i <= repeat_any_args->max; i++) { if (bytes_matched + i >= max_bytes_matched) break; next_input = input + i * input_incr; if (*(next_opcode) != RE_OPCODE_LITERAL || (*(next_opcode) == RE_OPCODE_LITERAL && *(next_opcode + 1) == *next_input)) { if (sp >= YR_MAX_FAST_RE_STACK) return ERROR_TOO_MANY_RE_FIBERS; code_stack[sp] = next_opcode; input_stack[sp] = next_input; matches_stack[sp] = bytes_matched + i; sp++; } } input += input_incr * repeat_any_args->min; bytes_matched += repeat_any_args->min; ip = next_opcode; break; default: assert(false); } } } if (matches != NULL) *matches = -1; return ERROR_SUCCESS; } static void _yr_re_print_node(RE_NODE* re_node, uint32_t indent) { RE_NODE* child; int i; if (re_node == NULL) return; if (indent > 0) printf("\n%*s", indent, " "); switch (re_node->type) { case RE_NODE_ALT: printf("Alt("); _yr_re_print_node(re_node->children_head, indent + 4); printf(","); _yr_re_print_node(re_node->children_tail, indent + 4); printf("\n%*s%s", indent, " ", ")"); break; case RE_NODE_CONCAT: printf("Cat("); child = re_node->children_head; while (child != NULL) { _yr_re_print_node(child, indent + 4); printf(","); child = child->next_sibling; } printf("\n%*s%s", indent, " ", ")"); break; case RE_NODE_STAR: printf("Star("); _yr_re_print_node(re_node->children_head, indent + 4); printf(")"); break; case RE_NODE_PLUS: printf("Plus("); _yr_re_print_node(re_node->children_head, indent + 4); printf(")"); break; case RE_NODE_LITERAL: printf("Lit(%c)", re_node->value); break; case RE_NODE_MASKED_LITERAL: printf("MaskedLit(%02X,%02X)", re_node->value, re_node->mask); break; case RE_NODE_WORD_CHAR: printf("WordChar"); break; case RE_NODE_NON_WORD_CHAR: printf("NonWordChar"); break; case RE_NODE_SPACE: printf("Space"); break; case RE_NODE_NON_SPACE: printf("NonSpace"); break; case RE_NODE_DIGIT: printf("Digit"); break; case RE_NODE_NON_DIGIT: printf("NonDigit"); break; case RE_NODE_ANY: printf("Any"); break; case RE_NODE_RANGE: printf("Range(%d-%d, ", re_node->start, re_node->end); _yr_re_print_node(re_node->children_head, indent + 4); printf("\n%*s%s", indent, " ", ")"); break; case RE_NODE_CLASS: printf("Class("); for (i = 0; i < 256; i++) if (_yr_re_is_char_in_class(re_node->re_class, i, false)) printf("%02X,", i); printf(")"); break; default: printf("???"); break; } } void yr_re_print(RE_AST* re_ast) { _yr_re_print_node(re_ast->root_node, 0); } yara-4.1.3/libyara/re_grammar.c000066400000000000000000001575361413423160300163740ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse re_yyparse #define yylex re_yylex #define yyerror re_yyerror #define yydebug re_yydebug #define yynerrs re_yynerrs /* Copy the first part of user declarations. */ #line 30 "re_grammar.y" /* yacc.c:339 */ #include #include #include #include #include #include #include #define YYERROR_VERBOSE #define YYMALLOC yr_malloc #define YYFREE yr_free #define mark_as_not_fast_regexp() \ ((RE_AST *) yyget_extra(yyscanner))->flags &= ~RE_FLAGS_FAST_REGEXP #define fail_if(x, error) \ if (x) \ { \ lex_env->last_error = error; \ YYABORT; \ } #define destroy_node_if(x, node) \ if (x) \ { \ yr_re_node_destroy(node); \ } #line 106 "re_grammar.c" /* yacc.c:339 */ #ifndef YY_NULLPTR #if defined __cplusplus && 201103L <= __cplusplus #define YY_NULLPTR nullptr #else #define YY_NULLPTR 0 #endif #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE #undef YYERROR_VERBOSE #define YYERROR_VERBOSE 1 #else #define YYERROR_VERBOSE 0 #endif /* In a future release of Bison, this section will be replaced by #include "y.tab.h". */ #ifndef YY_RE_YY_RE_GRAMMAR_H_INCLUDED #define YY_RE_YY_RE_GRAMMAR_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG #define YYDEBUG 0 #endif #if YYDEBUG extern int re_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE #define YYTOKENTYPE enum yytokentype { _CHAR_ = 258, _ANY_ = 259, _RANGE_ = 260, _CLASS_ = 261, _WORD_CHAR_ = 262, _NON_WORD_CHAR_ = 263, _SPACE_ = 264, _NON_SPACE_ = 265, _DIGIT_ = 266, _NON_DIGIT_ = 267, _WORD_BOUNDARY_ = 268, _NON_WORD_BOUNDARY_ = 269 }; #endif /* Tokens. */ #define _CHAR_ 258 #define _ANY_ 259 #define _RANGE_ 260 #define _CLASS_ 261 #define _WORD_CHAR_ 262 #define _NON_WORD_CHAR_ 263 #define _SPACE_ 264 #define _NON_SPACE_ 265 #define _DIGIT_ 266 #define _NON_DIGIT_ 267 #define _WORD_BOUNDARY_ 268 #define _NON_WORD_BOUNDARY_ 269 /* Value type. */ #if !defined YYSTYPE && !defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 73 "re_grammar.y" /* yacc.c:355 */ int integer; uint32_t range; RE_NODE *re_node; RE_CLASS *re_class; #line 181 "re_grammar.c" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; #define YYSTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_DECLARED 1 #endif int re_yyparse(void *yyscanner, RE_LEX_ENVIRONMENT *lex_env); #endif /* !YY_RE_YY_RE_GRAMMAR_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 197 "re_grammar.c" /* yacc.c:358 */ #ifdef short #undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T #ifdef __SIZE_TYPE__ #define YYSIZE_T __SIZE_TYPE__ #elif defined size_t #define YYSIZE_T size_t #elif !defined YYSIZE_T #include /* INFRINGES ON USER NAME SPACE */ #define YYSIZE_T size_t #else #define YYSIZE_T unsigned int #endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ #if defined YYENABLE_NLS && YYENABLE_NLS #if ENABLE_NLS #include /* INFRINGES ON USER NAME SPACE */ #define YY_(Msgid) dgettext("bison-runtime", Msgid) #endif #endif #ifndef YY_ #define YY_(Msgid) Msgid #endif #endif #ifndef YY_ATTRIBUTE #if ( \ defined __GNUC__ && \ (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) || \ defined __SUNPRO_C && 0x5110 <= __SUNPRO_C #define YY_ATTRIBUTE(Spec) __attribute__(Spec) #else #define YY_ATTRIBUTE(Spec) /* empty */ #endif #endif #ifndef YY_ATTRIBUTE_PURE #define YY_ATTRIBUTE_PURE YY_ATTRIBUTE((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED #define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE((__unused__)) #endif #if !defined _Noreturn && \ (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) #if defined _MSC_VER && 1200 <= _MSC_VER #define _Noreturn __declspec(noreturn) #else #define _Noreturn YY_ATTRIBUTE((__noreturn__)) #endif #endif /* Suppress unused-variable warnings by "using" E. */ #if !defined lint || defined __GNUC__ #define YYUSE(E) ((void) (E)) #else #define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ #define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") \ _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") #define YY_IGNORE_MAYBE_UNINITIALIZED_END _Pragma("GCC diagnostic pop") #else #define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN #define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN #define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE #define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if !defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ #ifdef YYSTACK_USE_ALLOCA #if YYSTACK_USE_ALLOCA #ifdef __GNUC__ #define YYSTACK_ALLOC __builtin_alloca #elif defined __BUILTIN_VA_ARG_INCR #include /* INFRINGES ON USER NAME SPACE */ #elif defined _AIX #define YYSTACK_ALLOC __alloca #elif defined _MSC_VER #include /* INFRINGES ON USER NAME SPACE */ #define alloca _alloca #else #define YYSTACK_ALLOC alloca #if !defined _ALLOCA_H && !defined EXIT_SUCCESS #include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #endif #endif #endif #endif #ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ #define YYSTACK_FREE(Ptr) \ do \ { /* empty */ \ ; \ } while (0) #ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ #define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ #endif #else #define YYSTACK_ALLOC YYMALLOC #define YYSTACK_FREE YYFREE #ifndef YYSTACK_ALLOC_MAXIMUM #define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM #endif #if ( \ defined __cplusplus && !defined EXIT_SUCCESS && \ !((defined YYMALLOC || defined malloc) && \ (defined YYFREE || defined free))) #include /* INFRINGES ON USER NAME SPACE */ #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #endif #ifndef YYMALLOC #define YYMALLOC malloc #if !defined malloc && !defined EXIT_SUCCESS void *malloc(YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ #endif #endif #ifndef YYFREE #define YYFREE free #if !defined free && !defined EXIT_SUCCESS void free(void *); /* INFRINGES ON USER NAME SPACE */ #endif #endif #endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if ( \ !defined yyoverflow && \ (!defined __cplusplus || \ (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ #define YYSTACK_GAP_MAXIMUM (sizeof(union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ #define YYSTACK_BYTES(N) \ ((N) * (sizeof(yytype_int16) + sizeof(YYSTYPE)) + YYSTACK_GAP_MAXIMUM) #define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ #define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY(&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof(*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof(*yyptr); \ } while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ #ifndef YYCOPY #if defined __GNUC__ && 1 < __GNUC__ #define YYCOPY(Dst, Src, Count) \ __builtin_memcpy(Dst, Src, (Count) * sizeof(*(Src))) #else #define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) (Dst)[yyi] = (Src)[yyi]; \ } while (0) #endif #endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 22 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 45 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 24 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 6 /* YYNRULES -- Number of rules. */ #define YYNRULES 31 /* YYNSTATES -- Number of states. */ #define YYNSTATES 35 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 269 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, 2, 2, 21, 22, 16, 18, 2, 2, 23, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 17, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = {0, 104, 104, 109, 113, 117, 131, 155, 164, 172, 188, 206, 222, 239, 262, 286, 309, 333, 337, 343, 349, 355, 364, 368, 377, 386, 392, 398, 404, 410, 416, 422}; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "_CHAR_", "_ANY_", "_RANGE_", "_CLASS_", "_WORD_CHAR_", "_NON_WORD_CHAR_", "_SPACE_", "_NON_SPACE_", "_DIGIT_", "_NON_DIGIT_", "_WORD_BOUNDARY_", "_NON_WORD_BOUNDARY_", "'|'", "'*'", "'?'", "'+'", "'^'", "'$'", "'('", "')'", "'.'", "$accept", "re", "alternative", "concatenation", "repeat", "single", YY_NULLPTR}; #endif #ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = {0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 124, 42, 63, 43, 94, 36, 40, 41, 46}; #endif #define YYPACT_NINF -12 #define yypact_value_is_default(Yystate) (!!((Yystate) == (-12))) #define YYTABLE_NINF -1 #define yytable_value_is_error(Yytable_value) 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { -1, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, 18, -12, 1, -11, 18, -12, -2, 21, -12, 18, -12, 0, 16, 17, 23, -12, 18, -12, -12, -12, -12}; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 3, 24, 31, 25, 26, 27, 28, 29, 30, 18, 19, 20, 21, 0, 23, 0, 2, 4, 7, 17, 0, 1, 6, 8, 15, 9, 13, 11, 22, 5, 16, 10, 14, 12}; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = {-12, -12, 28, 22, 5, -12}; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = {-1, 16, 17, 18, 19, 20}; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { 1, 22, 2, 25, 23, 3, 4, 5, 6, 7, 8, 9, 10, 11, 26, 27, 28, 31, 12, 13, 14, 2, 15, 24, 3, 4, 5, 6, 7, 8, 9, 10, 11, 32, 33, 24, 23, 12, 13, 14, 34, 15, 21, 29, 0, 30}; static const yytype_int8 yycheck[] = { 1, 0, 3, 5, 15, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 17, 19, 20, 21, 3, 23, 18, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 17, 30, 15, 19, 20, 21, 17, 23, 14, 22, -1, 23}; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 19, 20, 21, 23, 25, 26, 27, 28, 29, 26, 0, 15, 28, 5, 16, 17, 18, 22, 27, 17, 17, 17, 17}; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = {0, 24, 25, 25, 26, 26, 26, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = {0, 2, 1, 1, 1, 3, 2, 1, 2, 2, 3, 2, 3, 2, 3, 2, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1}; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK(yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror(yyscanner, lex_env, YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG #ifndef YYFPRINTF #include /* INFRINGES ON USER NAME SPACE */ #define YYFPRINTF fprintf #endif #define YYDPRINTF(Args) \ do \ { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT #define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif #define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do \ { \ if (yydebug) \ { \ YYFPRINTF(stderr, "%s ", Title); \ yy_symbol_print(stderr, Type, Value, yyscanner, lex_env); \ YYFPRINTF(stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print( FILE *yyoutput, int yytype, YYSTYPE const *const yyvaluep, void *yyscanner, RE_LEX_ENVIRONMENT *lex_env) { FILE *yyo = yyoutput; YYUSE(yyo); YYUSE(yyscanner); YYUSE(lex_env); if (!yyvaluep) return; #ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT(yyoutput, yytoknum[yytype], *yyvaluep); #endif YYUSE(yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print( FILE *yyoutput, int yytype, YYSTYPE const *const yyvaluep, void *yyscanner, RE_LEX_ENVIRONMENT *lex_env) { YYFPRINTF( yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print(yyoutput, yytype, yyvaluep, yyscanner, lex_env); YYFPRINTF(yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print(yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF(stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF(stderr, " %d", yybot); } YYFPRINTF(stderr, "\n"); } #define YY_STACK_PRINT(Bottom, Top) \ do \ { \ if (yydebug) \ yy_stack_print((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print( yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, void *yyscanner, RE_LEX_ENVIRONMENT *lex_env) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF( stderr, "Reducing stack by rule %d (line %" PRIu64 "):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF(stderr, " $%d = ", yyi + 1); yy_symbol_print( stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]), yyscanner, lex_env); YYFPRINTF(stderr, "\n"); } } #define YY_REDUCE_PRINT(Rule) \ do \ { \ if (yydebug) \ yy_reduce_print(yyssp, yyvsp, Rule, yyscanner, lex_env); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ #define YYDPRINTF(Args) #define YY_SYMBOL_PRINT(Title, Type, Value, Location) #define YY_STACK_PRINT(Bottom, Top) #define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH #define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH #define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE #ifndef yystrlen #if defined __GLIBC__ && defined _STRING_H #define yystrlen strlen #else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen(const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } #endif #endif #ifndef yystpcpy #if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE #define yystpcpy stpcpy #else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char *yystpcpy(char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } #endif #endif #ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr(char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes:; } if (!yyres) return yystrlen(yystr); return yystpcpy(yyres, yystr) - yyres; } #endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error( YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr(YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default(yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error(yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr(YY_NULLPTR, yytname[yyx]); if (!(yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { #define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_( 5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); #undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen(yyformat); if (!(yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (!(yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr(yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct( const char *yymsg, int yytype, YYSTYPE *yyvaluep, void *yyscanner, RE_LEX_ENVIRONMENT *lex_env) { YYUSE(yyvaluep); YYUSE(yyscanner); YYUSE(lex_env); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT(yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { case 6: /* _CLASS_ */ #line 96 "re_grammar.y" /* yacc.c:1258 */ { yr_free(((*yyvaluep).re_class)); ((*yyvaluep).re_class) = NULL; } #line 1046 "re_grammar.c" /* yacc.c:1258 */ break; case 26: /* alternative */ #line 97 "re_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1052 "re_grammar.c" /* yacc.c:1258 */ break; case 27: /* concatenation */ #line 98 "re_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1058 "re_grammar.c" /* yacc.c:1258 */ break; case 28: /* repeat */ #line 99 "re_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1064 "re_grammar.c" /* yacc.c:1258 */ break; case 29: /* single */ #line 100 "re_grammar.y" /* yacc.c:1258 */ { yr_re_node_destroy(((*yyvaluep).re_node)); ((*yyvaluep).re_node) = NULL; } #line 1070 "re_grammar.c" /* yacc.c:1258 */ break; default: break; } YY_IGNORE_MAYBE_UNINITIALIZED_END } /*----------. | yyparse. | `----------*/ int yyparse(void *yyscanner, RE_LEX_ENVIRONMENT *lex_env) { /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ /* Default value used for initialization, for pacifying older GCCs or non-GCC compilers. */ YY_INITIAL_VALUE(static YYSTYPE yyval_default;) YYSTYPE yylval YY_INITIAL_VALUE(= yyval_default); /* Number of syntax errors so far. */ int yynerrs; int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow( YY_("memory exhausted"), &yyss1, yysize * sizeof(*yyssp), &yyvs1, yysize * sizeof(*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ #ifndef YYSTACK_RELOCATE goto yyexhaustedlab; #else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC( YYSTACK_BYTES(yystacksize)); if (!yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE(yyss_alloc, yyss); YYSTACK_RELOCATE(yyvs_alloc, yyvs); #undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE(yyss1); } #endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF( (stderr, "Stack size increased to %" PRIu64 "\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default(yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF((stderr, "Reading a token: ")); yychar = yylex(&yylval, yyscanner, lex_env); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE(yychar); YY_SYMBOL_PRINT("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error(yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1 - yylen]; YY_REDUCE_PRINT(yyn); switch (yyn) { case 2: #line 105 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast = yyget_extra(yyscanner); re_ast->root_node = (yyvsp[0].re_node); } #line 1341 "re_grammar.c" /* yacc.c:1663 */ break; case 4: #line 114 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); } #line 1349 "re_grammar.c" /* yacc.c:1663 */ break; case 5: #line 118 "re_grammar.y" /* yacc.c:1663 */ { mark_as_not_fast_regexp(); (yyval.re_node) = yr_re_node_create(RE_NODE_ALT); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-2].re_node)); destroy_node_if((yyval.re_node) == NULL, (yyvsp[0].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-2].re_node)); yr_re_node_append_child((yyval.re_node), (yyvsp[0].re_node)); } #line 1367 "re_grammar.c" /* yacc.c:1663 */ break; case 6: #line 132 "re_grammar.y" /* yacc.c:1663 */ { RE_NODE *node; mark_as_not_fast_regexp(); node = yr_re_node_create(RE_NODE_EMPTY); destroy_node_if(node == NULL, (yyvsp[-1].re_node)); fail_if(node == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node) = yr_re_node_create(RE_NODE_ALT); destroy_node_if((yyval.re_node) == NULL, node); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-1].re_node)); yr_re_node_append_child((yyval.re_node), node); } #line 1392 "re_grammar.c" /* yacc.c:1663 */ break; case 7: #line 156 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_CONCAT); destroy_node_if((yyval.re_node) == NULL, (yyvsp[0].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[0].re_node)); } #line 1405 "re_grammar.c" /* yacc.c:1663 */ break; case 8: #line 165 "re_grammar.y" /* yacc.c:1663 */ { yr_re_node_append_child((yyvsp[-1].re_node), (yyvsp[0].re_node)); (yyval.re_node) = (yyvsp[-1].re_node); } #line 1414 "re_grammar.c" /* yacc.c:1663 */ break; case 9: #line 173 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; (yyval.re_node) = yr_re_node_create(RE_NODE_STAR); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-1].re_node)); } #line 1434 "re_grammar.c" /* yacc.c:1663 */ break; case 10: #line 189 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; (yyval.re_node) = yr_re_node_create(RE_NODE_STAR); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-2].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-2].re_node)); (yyval.re_node)->greedy = false; } #line 1456 "re_grammar.c" /* yacc.c:1663 */ break; case 11: #line 207 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; (yyval.re_node) = yr_re_node_create(RE_NODE_PLUS); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-1].re_node)); } #line 1476 "re_grammar.c" /* yacc.c:1663 */ break; case 12: #line 223 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; (yyval.re_node) = yr_re_node_create(RE_NODE_PLUS); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-2].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-2].re_node)); (yyval.re_node)->greedy = false; } #line 1497 "re_grammar.c" /* yacc.c:1663 */ break; case 13: #line 240 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; if ((yyvsp[-1].re_node)->type == RE_NODE_ANY) { (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-1].re_node)); } (yyval.re_node)->start = 0; (yyval.re_node)->end = 1; } #line 1524 "re_grammar.c" /* yacc.c:1663 */ break; case 14: #line 263 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; if ((yyvsp[-2].re_node)->type == RE_NODE_ANY) { (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, (yyvsp[-2].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-2].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-2].re_node)); } (yyval.re_node)->start = 0; (yyval.re_node)->end = 1; (yyval.re_node)->greedy = false; } #line 1552 "re_grammar.c" /* yacc.c:1663 */ break; case 15: #line 287 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; if ((yyvsp[-1].re_node)->type == RE_NODE_ANY) { (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-1].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-1].re_node)); } (yyval.re_node)->start = (yyvsp[0].range) & 0xFFFF; ; (yyval.re_node)->end = (yyvsp[0].range) >> 16; ; } #line 1579 "re_grammar.c" /* yacc.c:1663 */ break; case 16: #line 310 "re_grammar.y" /* yacc.c:1663 */ { RE_AST *re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; if ((yyvsp[-2].re_node)->type == RE_NODE_ANY) { (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, (yyvsp[-2].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); (yyval.re_node) = yr_re_node_create(RE_NODE_RANGE); destroy_node_if((yyval.re_node) == NULL, (yyvsp[-2].re_node)); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child((yyval.re_node), (yyvsp[-2].re_node)); } (yyval.re_node)->start = (yyvsp[-1].range) & 0xFFFF; ; (yyval.re_node)->end = (yyvsp[-1].range) >> 16; ; (yyval.re_node)->greedy = false; } #line 1607 "re_grammar.c" /* yacc.c:1663 */ break; case 17: #line 334 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[0].re_node); } #line 1615 "re_grammar.c" /* yacc.c:1663 */ break; case 18: #line 338 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_WORD_BOUNDARY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1625 "re_grammar.c" /* yacc.c:1663 */ break; case 19: #line 344 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_NON_WORD_BOUNDARY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1635 "re_grammar.c" /* yacc.c:1663 */ break; case 20: #line 350 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_ANCHOR_START); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1645 "re_grammar.c" /* yacc.c:1663 */ break; case 21: #line 356 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_ANCHOR_END); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1655 "re_grammar.c" /* yacc.c:1663 */ break; case 22: #line 365 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = (yyvsp[-1].re_node); } #line 1663 "re_grammar.c" /* yacc.c:1663 */ break; case 23: #line 369 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_ANY); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->value = 0x00; (yyval.re_node)->mask = 0x00; } #line 1676 "re_grammar.c" /* yacc.c:1663 */ break; case 24: #line 378 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_LITERAL); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->value = (yyvsp[0].integer); (yyval.re_node)->mask = 0xFF; } #line 1689 "re_grammar.c" /* yacc.c:1663 */ break; case 25: #line 387 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_WORD_CHAR); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1699 "re_grammar.c" /* yacc.c:1663 */ break; case 26: #line 393 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_NON_WORD_CHAR); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1709 "re_grammar.c" /* yacc.c:1663 */ break; case 27: #line 399 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_SPACE); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1719 "re_grammar.c" /* yacc.c:1663 */ break; case 28: #line 405 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_NON_SPACE); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1729 "re_grammar.c" /* yacc.c:1663 */ break; case 29: #line 411 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_DIGIT); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1739 "re_grammar.c" /* yacc.c:1663 */ break; case 30: #line 417 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_NON_DIGIT); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); } #line 1749 "re_grammar.c" /* yacc.c:1663 */ break; case 31: #line 423 "re_grammar.y" /* yacc.c:1663 */ { (yyval.re_node) = yr_re_node_create(RE_NODE_CLASS); fail_if((yyval.re_node) == NULL, ERROR_INSUFFICIENT_MEMORY); (yyval.re_node)->re_class = (yyvsp[0].re_class); } #line 1761 "re_grammar.c" /* yacc.c:1663 */ break; #line 1765 "re_grammar.c" /* yacc.c:1663 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK(yylen); yylen = 0; YY_STACK_PRINT(yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE(yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if !YYERROR_VERBOSE yyerror(yyscanner, lex_env, YY_("syntax error")); #else #define YYSYNTAX_ERROR yysyntax_error(&yymsg_alloc, &yymsg, yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE(yymsg); yymsg = (char *) YYSTACK_ALLOC(yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror(yyscanner, lex_env, yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } #undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct("Error: discarding", yytoken, &yylval, yyscanner, lex_env); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK(yylen); yylen = 0; YY_STACK_PRINT(yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default(yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct("Error: popping", yystos[yystate], yyvsp, yyscanner, lex_env); YYPOPSTACK(1); yystate = *yyssp; YY_STACK_PRINT(yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror(yyscanner, lex_env, YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE(yychar); yydestruct( "Cleanup: discarding lookahead", yytoken, &yylval, yyscanner, lex_env); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK(yylen); YY_STACK_PRINT(yyss, yyssp); while (yyssp != yyss) { yydestruct("Cleanup: popping", yystos[*yyssp], yyvsp, yyscanner, lex_env); YYPOPSTACK(1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE(yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE(yymsg); #endif return yyresult; } #line 431 "re_grammar.y" /* yacc.c:1907 */ yara-4.1.3/libyara/re_grammar.h000066400000000000000000000055201413423160300163620ustar00rootroot00000000000000/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ #ifndef YY_RE_YY_RE_GRAMMAR_H_INCLUDED #define YY_RE_YY_RE_GRAMMAR_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG #define YYDEBUG 0 #endif #if YYDEBUG extern int re_yydebug; #endif /* Token type. */ #ifndef YYTOKENTYPE #define YYTOKENTYPE enum yytokentype { _CHAR_ = 258, _ANY_ = 259, _RANGE_ = 260, _CLASS_ = 261, _WORD_CHAR_ = 262, _NON_WORD_CHAR_ = 263, _SPACE_ = 264, _NON_SPACE_ = 265, _DIGIT_ = 266, _NON_DIGIT_ = 267, _WORD_BOUNDARY_ = 268, _NON_WORD_BOUNDARY_ = 269 }; #endif /* Tokens. */ #define _CHAR_ 258 #define _ANY_ 259 #define _RANGE_ 260 #define _CLASS_ 261 #define _WORD_CHAR_ 262 #define _NON_WORD_CHAR_ 263 #define _SPACE_ 264 #define _NON_SPACE_ 265 #define _DIGIT_ 266 #define _NON_DIGIT_ 267 #define _WORD_BOUNDARY_ 268 #define _NON_WORD_BOUNDARY_ 269 /* Value type. */ #if !defined YYSTYPE && !defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 73 "re_grammar.y" /* yacc.c:1916 */ int integer; uint32_t range; RE_NODE* re_node; RE_CLASS* re_class; #line 89 "re_grammar.h" /* yacc.c:1916 */ }; typedef union YYSTYPE YYSTYPE; #define YYSTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_DECLARED 1 #endif int re_yyparse(void* yyscanner, RE_LEX_ENVIRONMENT* lex_env); #endif /* !YY_RE_YY_RE_GRAMMAR_H_INCLUDED */ yara-4.1.3/libyara/re_grammar.y000066400000000000000000000246201413423160300164050ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ %{ #include #include #include #include #include #include #include #define YYERROR_VERBOSE #define YYMALLOC yr_malloc #define YYFREE yr_free #define mark_as_not_fast_regexp() \ ((RE_AST*) yyget_extra(yyscanner))->flags &= ~RE_FLAGS_FAST_REGEXP #define fail_if(x, error) \ if (x) \ { \ lex_env->last_error = error; \ YYABORT; \ } \ #define destroy_node_if(x, node) \ if (x) \ { \ yr_re_node_destroy(node); \ } \ %} %name-prefix "re_yy" %pure-parser %parse-param {void *yyscanner} %parse-param {RE_LEX_ENVIRONMENT *lex_env} %lex-param {yyscan_t yyscanner} %lex-param {RE_LEX_ENVIRONMENT *lex_env} %union { int integer; uint32_t range; RE_NODE* re_node; RE_CLASS* re_class; } %token _CHAR_ _ANY_ %token _RANGE_ %token _CLASS_ %token _WORD_CHAR_ %token _NON_WORD_CHAR_ %token _SPACE_ %token _NON_SPACE_ %token _DIGIT_ %token _NON_DIGIT_ %token _WORD_BOUNDARY_ %token _NON_WORD_BOUNDARY_ %type alternative concatenation repeat single %destructor { yr_free($$); $$ = NULL; } _CLASS_ %destructor { yr_re_node_destroy($$); $$ = NULL; } alternative %destructor { yr_re_node_destroy($$); $$ = NULL; } concatenation %destructor { yr_re_node_destroy($$); $$ = NULL; } repeat %destructor { yr_re_node_destroy($$); $$ = NULL; } single %% re : alternative { RE_AST* re_ast = yyget_extra(yyscanner); re_ast->root_node = $1; } | error ; alternative : concatenation { $$ = $1; } | alternative '|' concatenation { mark_as_not_fast_regexp(); $$ = yr_re_node_create(RE_NODE_ALT); destroy_node_if($$ == NULL, $1); destroy_node_if($$ == NULL, $3); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); yr_re_node_append_child($$, $3); } | alternative '|' { RE_NODE* node; mark_as_not_fast_regexp(); node = yr_re_node_create(RE_NODE_EMPTY); destroy_node_if(node == NULL, $1); fail_if(node == NULL, ERROR_INSUFFICIENT_MEMORY); $$ = yr_re_node_create(RE_NODE_ALT); destroy_node_if($$ == NULL, node); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); yr_re_node_append_child($$, node); } ; concatenation : repeat { $$ = yr_re_node_create(RE_NODE_CONCAT); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } | concatenation repeat { yr_re_node_append_child($1, $2); $$ = $1; } ; repeat : single '*' { RE_AST* re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; $$ = yr_re_node_create(RE_NODE_STAR); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } | single '*' '?' { RE_AST* re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; $$ = yr_re_node_create(RE_NODE_STAR); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); $$->greedy = false; } | single '+' { RE_AST* re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; $$ = yr_re_node_create(RE_NODE_PLUS); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } | single '+' '?' { RE_AST* re_ast; mark_as_not_fast_regexp(); re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; $$ = yr_re_node_create(RE_NODE_PLUS); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); $$->greedy = false; } | single '?' { RE_AST* re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; if ($1->type == RE_NODE_ANY) { $$ = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); $$ = yr_re_node_create(RE_NODE_RANGE); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } $$->start = 0; $$->end = 1; } | single '?' '?' { RE_AST* re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; if ($1->type == RE_NODE_ANY) { $$ = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); $$ = yr_re_node_create(RE_NODE_RANGE); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } $$->start = 0; $$->end = 1; $$->greedy = false; } | single _RANGE_ { RE_AST* re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_GREEDY; if ($1->type == RE_NODE_ANY) { $$ = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); $$ = yr_re_node_create(RE_NODE_RANGE); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } $$->start = $2 & 0xFFFF;; $$->end = $2 >> 16;; } | single _RANGE_ '?' { RE_AST* re_ast = yyget_extra(yyscanner); re_ast->flags |= RE_FLAGS_UNGREEDY; if ($1->type == RE_NODE_ANY) { $$ = yr_re_node_create(RE_NODE_RANGE_ANY); destroy_node_if(true, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } else { mark_as_not_fast_regexp(); $$ = yr_re_node_create(RE_NODE_RANGE); destroy_node_if($$ == NULL, $1); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); yr_re_node_append_child($$, $1); } $$->start = $2 & 0xFFFF;; $$->end = $2 >> 16;; $$->greedy = false; } | single { $$ = $1; } | _WORD_BOUNDARY_ { $$ = yr_re_node_create(RE_NODE_WORD_BOUNDARY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _NON_WORD_BOUNDARY_ { $$ = yr_re_node_create(RE_NODE_NON_WORD_BOUNDARY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | '^' { $$ = yr_re_node_create(RE_NODE_ANCHOR_START); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | '$' { $$ = yr_re_node_create(RE_NODE_ANCHOR_END); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } ; single : '(' alternative ')' { $$ = $2; } | '.' { $$ = yr_re_node_create(RE_NODE_ANY); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->value = 0x00; $$->mask = 0x00; } | _CHAR_ { $$ = yr_re_node_create(RE_NODE_LITERAL); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->value = $1; $$->mask = 0xFF; } | _WORD_CHAR_ { $$ = yr_re_node_create(RE_NODE_WORD_CHAR); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _NON_WORD_CHAR_ { $$ = yr_re_node_create(RE_NODE_NON_WORD_CHAR); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _SPACE_ { $$ = yr_re_node_create(RE_NODE_SPACE); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _NON_SPACE_ { $$ = yr_re_node_create(RE_NODE_NON_SPACE); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _DIGIT_ { $$ = yr_re_node_create(RE_NODE_DIGIT); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _NON_DIGIT_ { $$ = yr_re_node_create(RE_NODE_NON_DIGIT); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); } | _CLASS_ { $$ = yr_re_node_create(RE_NODE_CLASS); fail_if($$ == NULL, ERROR_INSUFFICIENT_MEMORY); $$->re_class = $1; } ; %% yara-4.1.3/libyara/re_lexer.c000066400000000000000000002214401413423160300160470ustar00rootroot00000000000000#line 1 "re_lexer.c" #line 3 "re_lexer.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 4 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif #ifdef yy_create_buffer #define re_yy_create_buffer_ALREADY_DEFINED #else #define yy_create_buffer re_yy_create_buffer #endif #ifdef yy_delete_buffer #define re_yy_delete_buffer_ALREADY_DEFINED #else #define yy_delete_buffer re_yy_delete_buffer #endif #ifdef yy_scan_buffer #define re_yy_scan_buffer_ALREADY_DEFINED #else #define yy_scan_buffer re_yy_scan_buffer #endif #ifdef yy_scan_string #define re_yy_scan_string_ALREADY_DEFINED #else #define yy_scan_string re_yy_scan_string #endif #ifdef yy_scan_bytes #define re_yy_scan_bytes_ALREADY_DEFINED #else #define yy_scan_bytes re_yy_scan_bytes #endif #ifdef yy_init_buffer #define re_yy_init_buffer_ALREADY_DEFINED #else #define yy_init_buffer re_yy_init_buffer #endif #ifdef yy_flush_buffer #define re_yy_flush_buffer_ALREADY_DEFINED #else #define yy_flush_buffer re_yy_flush_buffer #endif #ifdef yy_load_buffer_state #define re_yy_load_buffer_state_ALREADY_DEFINED #else #define yy_load_buffer_state re_yy_load_buffer_state #endif #ifdef yy_switch_to_buffer #define re_yy_switch_to_buffer_ALREADY_DEFINED #else #define yy_switch_to_buffer re_yy_switch_to_buffer #endif #ifdef yypush_buffer_state #define re_yypush_buffer_state_ALREADY_DEFINED #else #define yypush_buffer_state re_yypush_buffer_state #endif #ifdef yypop_buffer_state #define re_yypop_buffer_state_ALREADY_DEFINED #else #define yypop_buffer_state re_yypop_buffer_state #endif #ifdef yyensure_buffer_stack #define re_yyensure_buffer_stack_ALREADY_DEFINED #else #define yyensure_buffer_stack re_yyensure_buffer_stack #endif #ifdef yylex #define re_yylex_ALREADY_DEFINED #else #define yylex re_yylex #endif #ifdef yyrestart #define re_yyrestart_ALREADY_DEFINED #else #define yyrestart re_yyrestart #endif #ifdef yylex_init #define re_yylex_init_ALREADY_DEFINED #else #define yylex_init re_yylex_init #endif #ifdef yylex_init_extra #define re_yylex_init_extra_ALREADY_DEFINED #else #define yylex_init_extra re_yylex_init_extra #endif #ifdef yylex_destroy #define re_yylex_destroy_ALREADY_DEFINED #else #define yylex_destroy re_yylex_destroy #endif #ifdef yyget_debug #define re_yyget_debug_ALREADY_DEFINED #else #define yyget_debug re_yyget_debug #endif #ifdef yyset_debug #define re_yyset_debug_ALREADY_DEFINED #else #define yyset_debug re_yyset_debug #endif #ifdef yyget_extra #define re_yyget_extra_ALREADY_DEFINED #else #define yyget_extra re_yyget_extra #endif #ifdef yyset_extra #define re_yyset_extra_ALREADY_DEFINED #else #define yyset_extra re_yyset_extra #endif #ifdef yyget_in #define re_yyget_in_ALREADY_DEFINED #else #define yyget_in re_yyget_in #endif #ifdef yyset_in #define re_yyset_in_ALREADY_DEFINED #else #define yyset_in re_yyset_in #endif #ifdef yyget_out #define re_yyget_out_ALREADY_DEFINED #else #define yyget_out re_yyget_out #endif #ifdef yyset_out #define re_yyset_out_ALREADY_DEFINED #else #define yyset_out re_yyset_out #endif #ifdef yyget_leng #define re_yyget_leng_ALREADY_DEFINED #else #define yyget_leng re_yyget_leng #endif #ifdef yyget_text #define re_yyget_text_ALREADY_DEFINED #else #define yyget_text re_yyget_text #endif #ifdef yyget_lineno #define re_yyget_lineno_ALREADY_DEFINED #else #define yyget_lineno re_yyget_lineno #endif #ifdef yyset_lineno #define re_yyset_lineno_ALREADY_DEFINED #else #define yyset_lineno re_yyset_lineno #endif #ifdef yyget_column #define re_yyget_column_ALREADY_DEFINED #else #define yyget_column re_yyget_column #endif #ifdef yyset_column #define re_yyset_column_ALREADY_DEFINED #else #define yyset_column re_yyset_column #endif #ifdef yywrap #define re_yywrap_ALREADY_DEFINED #else #define yywrap re_yywrap #endif #ifdef yyget_lval #define re_yyget_lval_ALREADY_DEFINED #else #define yyget_lval re_yyget_lval #endif #ifdef yyset_lval #define re_yyset_lval_ALREADY_DEFINED #else #define yyset_lval re_yyset_lval #endif #ifdef yyalloc #define re_yyalloc_ALREADY_DEFINED #else #define yyalloc re_yyalloc #endif #ifdef yyrealloc #define re_yyrealloc_ALREADY_DEFINED #else #define yyrealloc re_yyrealloc #endif #ifdef yyfree #define re_yyfree_ALREADY_DEFINED #else #define yyfree re_yyfree #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #ifndef SIZE_MAX #define SIZE_MAX (~(size_t)0) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* begin standard C++ headers. */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an * integer in range [0..255] for use as an array index. */ #define YY_SC_TO_UI(c) ((YY_CHAR) (c)) /* An opaque pointer. */ #ifndef YY_TYPEDEF_YY_SCANNER_T #define YY_TYPEDEF_YY_SCANNER_T typedef void* yyscan_t; #endif /* For convenience, these vars (plus the bison vars far below) are macros in the reentrant scanner. */ #define yyin yyg->yyin_r #define yyout yyg->yyout_r #define yyextra yyg->yyextra_r #define yyleng yyg->yyleng_r #define yytext yyg->yytext_r #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) #define yy_flex_debug yyg->yy_flex_debug_r /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yyg->yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yyg->yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin , yyscanner ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires * access to the local variable yy_act. Since yyless() is a macro, it would break * existing scanners that call yyless() from OUTSIDE yylex. * One obvious solution it to make yy_act a global. I tried that, and saw * a 5% performance hit in a non-yylineno scanner, because yy_act is * normally declared as a register variable-- so it is not worth it. */ #define YY_LESS_LINENO(n) \ do { \ int yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) #define YY_LINENO_REWIND_TO(dst) \ do {\ const char *p;\ for ( p = yy_cp-1; p >= (dst); --p)\ if ( *p == '\n' )\ --yylineno;\ }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = yyg->yy_hold_char; \ YY_RESTORE_YY_MORE_OFFSET \ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] void yyrestart ( FILE *input_file , yyscan_t yyscanner ); void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); void yypop_buffer_state ( yyscan_t yyscanner ); static void yyensure_buffer_stack ( yyscan_t yyscanner ); static void yy_load_buffer_state ( yyscan_t yyscanner ); static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); #define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); void *yyalloc ( yy_size_t , yyscan_t yyscanner ); void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); void yyfree ( void * , yyscan_t yyscanner ); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ yyensure_buffer_stack (yyscanner); \ YY_CURRENT_BUFFER_LVALUE = \ yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define re_yywrap(yyscanner) (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef flex_uint8_t YY_CHAR; typedef int yy_state_type; #define yytext_ptr yytext_r static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); static int yy_get_next_buffer ( yyscan_t yyscanner ); static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yyg->yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yyg->yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yyg->yy_c_buf_p = yy_cp; #define YY_NUM_RULES 29 #define YY_END_OF_BUFFER 30 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static const flex_int16_t yy_accept[45] = { 0, 0, 0, 0, 0, 30, 7, 7, 28, 6, 17, 7, 27, 29, 26, 18, 5, 3, 16, 15, 13, 11, 9, 14, 12, 10, 8, 0, 0, 0, 0, 25, 23, 21, 24, 22, 20, 0, 4, 0, 1, 2, 19, 0, 0 } ; static const YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 3, 3, 3, 4, 5, 3, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 3, 1, 7, 8, 7, 9, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 11, 1, 1, 1, 12, 13, 14, 15, 1, 1, 7, 16, 7, 17, 7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18, 1, 1, 1, 19, 20, 1, 1, 21, 3, 22, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static const YY_CHAR yy_meta[23] = { 0, 1, 2, 1, 1, 3, 4, 4, 4, 4, 1, 1, 1, 1, 5, 1, 4, 4, 1, 1, 1, 1, 1 } ; static const flex_int16_t yy_base[51] = { 0, 0, 20, 3, 5, 50, 89, 89, 89, 10, 36, 0, 44, 43, 47, 38, 89, 26, 33, 89, 89, 89, 89, 89, 89, 89, 89, 4, 5, 0, 33, 32, 31, 29, 26, 24, 23, 15, 89, 8, 89, 89, 89, 0, 89, 67, 72, 77, 82, 84, 4 } ; static const flex_int16_t yy_def[51] = { 0, 45, 45, 46, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 47, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 48, 44, 44, 44, 44, 44, 44, 44, 49, 44, 44, 44, 44, 44, 50, 0, 44, 44, 44, 44, 44, 44 } ; static const flex_int16_t yy_nxt[112] = { 0, 44, 7, 8, 27, 13, 28, 13, 30, 27, 39, 28, 9, 10, 39, 8, 14, 15, 14, 15, 29, 11, 7, 8, 16, 17, 40, 41, 29, 29, 40, 29, 9, 10, 29, 8, 29, 29, 29, 18, 38, 11, 18, 29, 19, 20, 21, 22, 29, 29, 44, 44, 23, 24, 25, 26, 31, 32, 33, 44, 44, 44, 44, 44, 34, 35, 36, 37, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 30, 44, 30, 30, 30, 42, 42, 42, 42, 43, 43, 5, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44 } ; static const flex_int16_t yy_chk[112] = { 0, 0, 1, 1, 11, 3, 11, 4, 50, 28, 27, 28, 1, 1, 39, 1, 3, 3, 4, 4, 37, 1, 2, 2, 9, 9, 27, 28, 36, 35, 39, 34, 2, 2, 33, 2, 32, 31, 30, 18, 17, 2, 10, 15, 10, 10, 10, 10, 13, 12, 5, 0, 10, 10, 10, 10, 14, 14, 14, 0, 0, 0, 0, 0, 14, 14, 14, 14, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 47, 0, 47, 47, 47, 48, 48, 48, 48, 49, 49, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44 } ; /* Table of booleans, true if rule could match eol. */ static const flex_int32_t yy_rule_can_match_eol[30] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET #line 1 "re_lexer.l" /* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Lexical analyzer for regular expressions */ #line 33 "re_lexer.l" /* Disable warnings for unused functions in this file. As we redefine YY_FATAL_ERROR macro to use our own function re_yyfatal, the yy_fatal_error function generated by Flex is not actually used, causing a compiler warning. Flex doesn't offer any options to remove the yy_fatal_error function. When they include something like %option noyy_fatal_error as they do with noyywrap then we can remove this pragma. */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #define snprintf _snprintf #endif // Bitmap with 1 bit for each of the 256 characters in the ASCII table. The bit // is set to 1 if the corresponding character is alphanumeric or 0 if otherwise. static uint8_t word_chars[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Bitmap with 1 bit for each of the 256 characters in the ASCII table. The bit // is set to 1 if the corresponding character is considered a space. Space // characters include horizontal and vertical tabs, carriage return, new line // and form feed (\t, \v, \r, \n, \f). static uint8_t space_chars[] = { 0x00, 0x3E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int escaped_char_value( char* text, uint8_t* value); int read_escaped_char( yyscan_t yyscanner, uint8_t* escaped_char); #line 794 "re_lexer.c" #define YY_NO_UNISTD_H 1 #line 797 "re_lexer.c" #define INITIAL 0 #define char_class 1 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif /* Holds the entire state of the reentrant scanner. */ struct yyguts_t { /* User-defined. Not touched by flex. */ YY_EXTRA_TYPE yyextra_r; /* The rest are the same as the globals declared in the non-reentrant scanner. */ FILE *yyin_r, *yyout_r; size_t yy_buffer_stack_top; /**< index of top of stack. */ size_t yy_buffer_stack_max; /**< capacity of stack. */ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ char yy_hold_char; int yy_n_chars; int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; int yy_did_buffer_switch_on_eof; int yy_start_stack_ptr; int yy_start_stack_depth; int *yy_start_stack; yy_state_type yy_last_accepting_state; char* yy_last_accepting_cpos; int yylineno_r; int yy_flex_debug_r; char *yytext_r; int yy_more_flag; int yy_more_len; YYSTYPE * yylval_r; }; /* end struct yyguts_t */ static int yy_init_globals ( yyscan_t yyscanner ); /* This must go here because YYSTYPE and YYLTYPE are included * from bison output in section 1.*/ # define yylval yyg->yylval_r int yylex_init (yyscan_t* scanner); int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int yylex_destroy ( yyscan_t yyscanner ); int yyget_debug ( yyscan_t yyscanner ); void yyset_debug ( int debug_flag , yyscan_t yyscanner ); YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); FILE *yyget_in ( yyscan_t yyscanner ); void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); FILE *yyget_out ( yyscan_t yyscanner ); void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); int yyget_leng ( yyscan_t yyscanner ); char *yyget_text ( yyscan_t yyscanner ); int yyget_lineno ( yyscan_t yyscanner ); void yyset_lineno ( int _line_number , yyscan_t yyscanner ); int yyget_column ( yyscan_t yyscanner ); void yyset_column ( int _column_no , yyscan_t yyscanner ); YYSTYPE * yyget_lval ( yyscan_t yyscanner ); void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap ( yyscan_t yyscanner ); #else extern int yywrap ( yyscan_t yyscanner ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen ( const char * , yyscan_t yyscanner); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput ( yyscan_t yyscanner ); #else static int input ( yyscan_t yyscanner ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(yyin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner); #define YY_DECL int yylex \ (YYSTYPE * yylval_param , yyscan_t yyscanner) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; if ( !yyg->yy_init ) { yyg->yy_init = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! YY_CURRENT_BUFFER ) { yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_load_buffer_state( yyscanner ); } { #line 111 "re_lexer.l" #line 1073 "re_lexer.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; /* Support of yytext. */ *yy_cp = yyg->yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yyg->yy_start; yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 45 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } while ( yy_current_state != 44 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) { int yyl; for ( yyl = 0; yyl < yyleng; ++yyl ) if ( yytext[yyl] == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; } do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yyg->yy_hold_char; yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP #line 113 "re_lexer.l" { // Examples: {3,8} {0,5} {,5} {7,} int hi_bound; int lo_bound = atoi(yytext + 1); char* comma = strchr(yytext, ','); if (comma - yytext == strlen(yytext) - 2) // if comma is followed by the closing curly bracket // (example: {2,}) set high bound value to maximum. hi_bound = RE_MAX_RANGE; else hi_bound = atoi(comma + 1); if (hi_bound > RE_MAX_RANGE) { yyerror(yyscanner, lex_env, "repeat interval too large"); yyterminate(); } if (hi_bound < lo_bound || hi_bound < 0 || lo_bound < 0) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } if (hi_bound == 0 && lo_bound == 0) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } yylval->range = (hi_bound << 16) | lo_bound; return _RANGE_; } YY_BREAK case 2: YY_RULE_SETUP #line 153 "re_lexer.l" { // Example: {10} int value = atoi(yytext + 1); // atoi can return a negative value if the input string represents a number // too large to fit in an integer. if (value > RE_MAX_RANGE || value < 0) { yyerror(yyscanner, lex_env, "repeat interval too large"); yyterminate(); } if (value == 0) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } yylval->range = (value << 16) | value; return _RANGE_; } YY_BREAK case 3: YY_RULE_SETUP #line 180 "re_lexer.l" { // Start of a negated character class. Example: [^abcd] BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = true; } YY_BREAK case 4: YY_RULE_SETUP #line 189 "re_lexer.l" { // Start of character negated class containing a ]. // Example: [^]abc] this must be interpreted as a class // not matching ], a, b, nor c BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = true; LEX_ENV->re_class.bitmap[']' / 8] |= 1 << ']' % 8; } YY_BREAK case 5: YY_RULE_SETUP #line 202 "re_lexer.l" { // Start of character class containing a ]. // Example: []abc] this must be interpreted as a class // matching ], a, b, or c. BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = false; LEX_ENV->re_class.bitmap[']' / 8] |= 1 << ']' % 8; } YY_BREAK case 6: YY_RULE_SETUP #line 215 "re_lexer.l" { // Start of character class. Example: [abcd] BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = false; } YY_BREAK case 7: /* rule 7 can match eol */ YY_RULE_SETUP #line 225 "re_lexer.l" { // Any non-special character is passed as a CHAR token to the scanner. yylval->integer = yytext[0]; return _CHAR_; } YY_BREAK case 8: YY_RULE_SETUP #line 234 "re_lexer.l" { return _WORD_CHAR_; } YY_BREAK case 9: YY_RULE_SETUP #line 239 "re_lexer.l" { return _NON_WORD_CHAR_; } YY_BREAK case 10: YY_RULE_SETUP #line 244 "re_lexer.l" { return _SPACE_; } YY_BREAK case 11: YY_RULE_SETUP #line 249 "re_lexer.l" { return _NON_SPACE_; } YY_BREAK case 12: YY_RULE_SETUP #line 254 "re_lexer.l" { return _DIGIT_; } YY_BREAK case 13: YY_RULE_SETUP #line 259 "re_lexer.l" { return _NON_DIGIT_; } YY_BREAK case 14: YY_RULE_SETUP #line 264 "re_lexer.l" { return _WORD_BOUNDARY_; } YY_BREAK case 15: YY_RULE_SETUP #line 268 "re_lexer.l" { return _NON_WORD_BOUNDARY_; } YY_BREAK case 16: YY_RULE_SETUP #line 273 "re_lexer.l" { yyerror(yyscanner, lex_env, "backreferences are not allowed"); yyterminate(); } YY_BREAK case 17: YY_RULE_SETUP #line 280 "re_lexer.l" { uint8_t c; if (read_escaped_char(yyscanner, &c)) { yylval->integer = c; return _CHAR_; } else { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } } YY_BREAK case 18: YY_RULE_SETUP #line 297 "re_lexer.l" { // End of character class. yylval->re_class = (RE_CLASS*) yr_malloc(sizeof(RE_CLASS)); memcpy(yylval->re_class->bitmap, LEX_ENV->re_class.bitmap, 32); yylval->re_class->negated = LEX_ENV->re_class.negated; BEGIN(INITIAL); return _CLASS_; } YY_BREAK case 19: /* rule 19 can match eol */ YY_RULE_SETUP #line 311 "re_lexer.l" { // A range inside a character class. // [abc0-9] // ^- matching here uint16_t c; uint8_t start = yytext[0]; uint8_t end = yytext[2]; if (start == '\\') { if (!escaped_char_value(yytext, &start)) { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } if (yytext[1] == 'x') end = yytext[5]; else end = yytext[3]; } if (end == '\\') { if (!read_escaped_char(yyscanner, &end)) { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } } if (end < start) { yyerror(yyscanner, lex_env, "bad character range"); yyterminate(); } for (c = start; c <= end; c++) { LEX_ENV->re_class.bitmap[c / 8] |= 1 << c % 8; } } YY_BREAK case 20: YY_RULE_SETUP #line 357 "re_lexer.l" { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= word_chars[i]; } YY_BREAK case 21: YY_RULE_SETUP #line 366 "re_lexer.l" { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= ~word_chars[i]; } YY_BREAK case 22: YY_RULE_SETUP #line 375 "re_lexer.l" { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= space_chars[i]; } YY_BREAK case 23: YY_RULE_SETUP #line 384 "re_lexer.l" { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= ~space_chars[i]; } YY_BREAK case 24: YY_RULE_SETUP #line 393 "re_lexer.l" { char c; for (c = '0'; c <= '9'; c++) LEX_ENV->re_class.bitmap[c / 8] |= 1 << c % 8; } YY_BREAK case 25: YY_RULE_SETUP #line 402 "re_lexer.l" { int i; for (i = 0; i < 32; i++) { // digits 0-7 are in the sixth byte of the vector, let that byte alone if (i == 6) continue; // digits 8 and 9 are the lowest two bits in the seventh byte of the // vector, let those bits alone. if (i == 7) LEX_ENV->re_class.bitmap[i] |= 0xFC; else LEX_ENV->re_class.bitmap[i] = 0xFF; } } YY_BREAK case 26: YY_RULE_SETUP #line 422 "re_lexer.l" { uint8_t c; if (read_escaped_char(yyscanner, &c)) { LEX_ENV->re_class.bitmap[c / 8] |= 1 << c % 8; } else { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } } YY_BREAK case 27: YY_RULE_SETUP #line 438 "re_lexer.l" { if (yytext[0] >= 32 && yytext[0] < 127) { // A character class (i.e: [0-9a-f]) is represented by a 256-bits vector, // here we set to 1 the vector's bit corresponding to the input character. LEX_ENV->re_class.bitmap[yytext[0] / 8] |= 1 << yytext[0] % 8; } else { yyerror(yyscanner, lex_env, "non-ascii character"); yyterminate(); } } YY_BREAK case YY_STATE_EOF(char_class): #line 455 "re_lexer.l" { // End of regexp reached while scanning a character class. yyerror(yyscanner, lex_env, "missing terminating ] for character class"); yyterminate(); } YY_BREAK case 28: YY_RULE_SETUP #line 464 "re_lexer.l" { if (yytext[0] >= 32 && yytext[0] < 127) { return yytext[0]; } else { yyerror(yyscanner, lex_env, "non-ascii character"); yyterminate(); } } YY_BREAK case YY_STATE_EOF(INITIAL): #line 478 "re_lexer.l" { yyterminate(); } YY_BREAK case 29: YY_RULE_SETUP #line 483 "re_lexer.l" ECHO; YY_BREAK #line 1575 "re_lexer.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yyg->yy_hold_char; YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yyg->yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; goto yy_find_action; } } else switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_END_OF_FILE: { yyg->yy_did_buffer_switch_on_eof = 0; if ( yywrap( yyscanner ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yyg->yy_c_buf_p = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; yy_current_state = yy_get_previous_state( yyscanner ); yy_cp = yyg->yy_c_buf_p; yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = yyg->yytext_ptr; int number_to_move, i; int ret_val; if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) (yyg->yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yyrealloc( (void *) b->yy_ch_buf, (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), yyg->yy_n_chars, num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } if ( yyg->yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin , yyscanner); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); /* "- 2" to take care of EOB's */ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); } yyg->yy_n_chars += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (yyscan_t yyscanner) { yy_state_type yy_current_state; char *yy_cp; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_current_state = yyg->yy_start; for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 45 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { int yy_is_jam; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ char *yy_cp = yyg->yy_c_buf_p; YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; yyg->yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 45 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; yy_is_jam = (yy_current_state == 44); (void)yyg; return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (yyscan_t yyscanner) #else static int input (yyscan_t yyscanner) #endif { int c; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; *yyg->yy_c_buf_p = yyg->yy_hold_char; if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) /* This was really a NUL. */ *yyg->yy_c_buf_p = '\0'; else { /* need more input */ int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); ++yyg->yy_c_buf_p; switch ( yy_get_next_buffer( yyscanner ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ yyrestart( yyin , yyscanner); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( yywrap( yyscanner ) ) return 0; if ( ! yyg->yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(yyscanner); #else return input(yyscanner); #endif } case EOB_ACT_CONTINUE_SCAN: yyg->yy_c_buf_p = yyg->yytext_ptr + offset; break; } } } c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ yyg->yy_hold_char = *++yyg->yy_c_buf_p; if ( c == '\n' ) do{ yylineno++; yycolumn=0; }while(0) ; return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * @param yyscanner The scanner object. * @note This function does not reset the start condition to @c INITIAL . */ void yyrestart (FILE * input_file , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! YY_CURRENT_BUFFER ){ yyensure_buffer_stack (yyscanner); YY_CURRENT_BUFFER_LVALUE = yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); } yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); yy_load_buffer_state( yyscanner ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. */ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* TODO. We should be able to replace this entire function body * with * yypop_buffer_state(); * yypush_buffer_state(new_buffer); */ yyensure_buffer_stack (yyscanner); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } YY_CURRENT_BUFFER_LVALUE = new_buffer; yy_load_buffer_state( yyscanner ); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yyg->yy_did_buffer_switch_on_eof = 1; } static void yy_load_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; yyg->yy_hold_char = *yyg->yy_c_buf_p; } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * @param yyscanner The scanner object. * @return the allocated buffer state. */ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file , yyscanner); return b; } /** Destroy the buffer. * @param b a buffer created with yy_create_buffer() * @param yyscanner The scanner object. */ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yyfree( (void *) b->yy_ch_buf , yyscanner ); yyfree( (void *) b , yyscanner ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. */ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) { int oerrno = errno; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flush_buffer( b , yyscanner); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then yy_init_buffer was _probably_ * called from yyrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * @param yyscanner The scanner object. */ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) yy_load_buffer_state( yyscanner ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * @param yyscanner The scanner object. */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (new_buffer == NULL) return; yyensure_buffer_stack(yyscanner); /* This block is copied from yy_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *yyg->yy_c_buf_p = yyg->yy_hold_char; YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) yyg->yy_buffer_stack_top++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from yy_switch_to_buffer. */ yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * @param yyscanner The scanner object. */ void yypop_buffer_state (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!YY_CURRENT_BUFFER) return; yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); YY_CURRENT_BUFFER_LVALUE = NULL; if (yyg->yy_buffer_stack_top > 0) --yyg->yy_buffer_stack_top; if (YY_CURRENT_BUFFER) { yy_load_buffer_state( yyscanner ); yyg->yy_did_buffer_switch_on_eof = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void yyensure_buffer_stack (yyscan_t yyscanner) { yy_size_t num_to_alloc; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (!yyg->yy_buffer_stack) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc (num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; yyg->yy_buffer_stack_top = 0; return; } if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = yyg->yy_buffer_stack_max + grow_size; yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc (yyg->yy_buffer_stack, num_to_alloc * sizeof(struct yy_buffer_state*) , yyscanner); if ( ! yyg->yy_buffer_stack ) YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); /* zero only the new slots.*/ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); yyg->yy_buffer_stack_max = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b , yyscanner ); return b; } /** Setup the input buffer state to scan a string. The next call to yylex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * @param yyscanner The scanner object. * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * yy_scan_bytes() instead. */ YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) { return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); } /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * @param yyscanner The scanner object. * @return the newly allocated buffer state object. */ YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc( n , yyscanner ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n , yyscanner); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ int yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ yytext[yyleng] = yyg->yy_hold_char; \ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ yyg->yy_hold_char = *yyg->yy_c_buf_p; \ *yyg->yy_c_buf_p = '\0'; \ yyleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyextra; } /** Get the current line number. * @param yyscanner The scanner object. */ int yyget_lineno (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yylineno; } /** Get the current column number. * @param yyscanner The scanner object. */ int yyget_column (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (! YY_CURRENT_BUFFER) return 0; return yycolumn; } /** Get the input stream. * @param yyscanner The scanner object. */ FILE *yyget_in (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyin; } /** Get the output stream. * @param yyscanner The scanner object. */ FILE *yyget_out (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyout; } /** Get the length of the current token. * @param yyscanner The scanner object. */ int yyget_leng (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yyleng; } /** Get the current token. * @param yyscanner The scanner object. */ char *yyget_text (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yytext; } /** Set the user-defined data. This data is never touched by the scanner. * @param user_defined The data to be associated with this scanner. * @param yyscanner The scanner object. */ void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyextra = user_defined ; } /** Set the current line number. * @param _line_number line number * @param yyscanner The scanner object. */ void yyset_lineno (int _line_number , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* lineno is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); yylineno = _line_number; } /** Set the current column. * @param _column_no column number * @param yyscanner The scanner object. */ void yyset_column (int _column_no , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* column is only valid if an input buffer exists. */ if (! YY_CURRENT_BUFFER ) YY_FATAL_ERROR( "yyset_column called with no buffer" ); yycolumn = _column_no; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * @param yyscanner The scanner object. * @see yy_switch_to_buffer */ void yyset_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyin = _in_str ; } void yyset_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yyout = _out_str ; } int yyget_debug (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yy_flex_debug; } void yyset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yy_flex_debug = _bdebug ; } /* Accessor methods for yylval and yylloc */ YYSTYPE * yyget_lval (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; return yylval; } void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; yylval = yylval_param; } /* User-visible API */ /* yylex_init is special because it creates the scanner itself, so it is * the ONLY reentrant function that doesn't take the scanner as the last argument. * That's why we explicitly handle the declaration, instead of using our macros. */ int yylex_init(yyscan_t* ptr_yy_globals) { if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); return yy_init_globals ( *ptr_yy_globals ); } /* yylex_init_extra has the same functionality as yylex_init, but follows the * convention of taking the scanner as the last argument. Note however, that * this is a *pointer* to a scanner, as it will be allocated by this call (and * is the reason, too, why this function also must handle its own declaration). * The user defined value in the first argument will be available to yyalloc in * the yyextra field. */ int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) { struct yyguts_t dummy_yyguts; yyset_extra (yy_user_defined, &dummy_yyguts); if (ptr_yy_globals == NULL){ errno = EINVAL; return 1; } *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); if (*ptr_yy_globals == NULL){ errno = ENOMEM; return 1; } /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); yyset_extra (yy_user_defined, *ptr_yy_globals); return yy_init_globals ( *ptr_yy_globals ); } static int yy_init_globals (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Initialization is the same as for the non-reentrant scanner. * This function is called from yylex_destroy(), so don't allocate here. */ yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; yyg->yy_c_buf_p = NULL; yyg->yy_init = 0; yyg->yy_start = 0; yyg->yy_start_stack_ptr = 0; yyg->yy_start_stack_depth = 0; yyg->yy_start_stack = NULL; /* Defined in main.c */ #ifdef YY_STDINIT yyin = stdin; yyout = stdout; #else yyin = NULL; yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by * yylex_init() */ return 0; } /* yylex_destroy is for both reentrant and non-reentrant scanners. */ int yylex_destroy (yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); YY_CURRENT_BUFFER_LVALUE = NULL; yypop_buffer_state(yyscanner); } /* Destroy the stack itself. */ yyfree(yyg->yy_buffer_stack , yyscanner); yyg->yy_buffer_stack = NULL; /* Destroy the start condition stack. */ yyfree( yyg->yy_start_stack , yyscanner ); yyg->yy_start_stack = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * yylex() is called, initialization will occur. */ yy_init_globals( yyscanner); /* Destroy the main struct (reentrant only). */ yyfree ( yyscanner , yyscanner ); yyscanner = NULL; return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (const char * s , yyscan_t yyscanner) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *yyalloc (yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; return malloc(size); } void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void yyfree (void * ptr , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; (void)yyg; free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 483 "re_lexer.l" int escaped_char_value( char* text, uint8_t* value) { unsigned int hex_value; char hex[3]; assert(text[0] == '\\'); switch(text[1]) { case 'x': if (!isxdigit(text[2]) || !isxdigit(text[3])) return 0; hex[0] = text[2]; hex[1] = text[3]; hex[2] = '\0'; sscanf(hex, "%x", &hex_value); *value = (uint8_t) hex_value; break; case 'n': *value = '\n'; break; case 't': *value = '\t'; break; case 'r': *value = '\r'; break; case 'f': *value = '\f'; break; case 'a': *value = '\a'; break; default: *value = text[1]; } return 1; } #ifdef __cplusplus #define RE_YY_INPUT yyinput #else #define RE_YY_INPUT input #endif int read_escaped_char( yyscan_t yyscanner, uint8_t* escaped_char) { char text[4] = {0, 0, 0, 0}; text[0] = '\\'; text[1] = RE_YY_INPUT(yyscanner); if (text[1] == EOF || text[1] == 0) return 0; if (text[1] == 'x') { text[2] = RE_YY_INPUT(yyscanner); if (text[2] == EOF || text[2] == 0) return 0; text[3] = RE_YY_INPUT(yyscanner); if (text[3] == EOF || text[3] == 0) return 0; } return escaped_char_value(text, escaped_char); } // // yyfatal (actually named re_yyfatal because of the '%option prefix="re_yy"' // directive) is called when a fatal error occurs in the parser. When this // happens we are deep inside the parsing logic generated by flex/bison and // the only way to exit gracefully from there is using setjmp/longjmp. // void yyfatal( yyscan_t yyscanner, const char *error_message) { jmp_buf* recovery_trampoline = (jmp_buf*) yr_thread_storage_get_value( &yr_yyfatal_trampoline_tls); longjmp(*recovery_trampoline, 1); } void yyerror( yyscan_t yyscanner, RE_LEX_ENVIRONMENT* lex_env, const char *error_message) { // if lex_env->last_error was set to some error code before // don't overwrite it, we are interested in the first error, not in // subsequent errors like "syntax error, unexpected $end" caused by // early parser termination. if (lex_env->last_error == ERROR_SUCCESS) { lex_env->last_error = ERROR_INVALID_REGULAR_EXPRESSION; strlcpy( lex_env->last_error_message, error_message, sizeof(lex_env->last_error_message)); } } int yr_parse_re_string( const char* re_string, RE_AST** re_ast, RE_ERROR* error) { yyscan_t yyscanner; jmp_buf recovery_trampoline; RE_LEX_ENVIRONMENT lex_env; lex_env.last_error = ERROR_SUCCESS; lex_env.last_error_message[0] = '\0'; yr_thread_storage_set_value( &yr_yyfatal_trampoline_tls, &recovery_trampoline); // setjmp returns a non-zero value only when we are returning to this // point via a call to longjmp to the recovery trampoline. if (setjmp(recovery_trampoline) != 0) return ERROR_INTERNAL_FATAL_ERROR; FAIL_ON_ERROR(yr_re_ast_create(re_ast)); yylex_init(&yyscanner); yyset_extra(*re_ast, yyscanner); yy_scan_string(re_string, yyscanner); yyparse(yyscanner, &lex_env); yylex_destroy(yyscanner); if (lex_env.last_error != ERROR_SUCCESS) { yr_re_ast_destroy(*re_ast); *re_ast = NULL; strlcpy( error->message, lex_env.last_error_message, sizeof(error->message)); return lex_env.last_error; } return ERROR_SUCCESS; } yara-4.1.3/libyara/re_lexer.l000066400000000000000000000314371413423160300160650ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Lexical analyzer for regular expressions */ %{ /* Disable warnings for unused functions in this file. As we redefine YY_FATAL_ERROR macro to use our own function re_yyfatal, the yy_fatal_error function generated by Flex is not actually used, causing a compiler warning. Flex doesn't offer any options to remove the yy_fatal_error function. When they include something like %option noyy_fatal_error as they do with noyywrap then we can remove this pragma. */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #define snprintf _snprintf #endif // Bitmap with 1 bit for each of the 256 characters in the ASCII table. The bit // is set to 1 if the corresponding character is alphanumeric or 0 if otherwise. static uint8_t word_chars[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Bitmap with 1 bit for each of the 256 characters in the ASCII table. The bit // is set to 1 if the corresponding character is considered a space. Space // characters include horizontal and vertical tabs, carriage return, new line // and form feed (\t, \v, \r, \n, \f). static uint8_t space_chars[] = { 0x00, 0x3E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int escaped_char_value( char* text, uint8_t* value); int read_escaped_char( yyscan_t yyscanner, uint8_t* escaped_char); %} %option reentrant bison-bridge %option noyywrap %option nounistd %option nounput %option never-interactive %option yylineno %option prefix="re_yy" %option outfile="lex.yy.c" %option verbose %option warn %x char_class digit [0-9] hex_digit [0-9a-fA-F] %% \{{digit}*,{digit}*\} { // Examples: {3,8} {0,5} {,5} {7,} int hi_bound; int lo_bound = atoi(yytext + 1); char* comma = strchr(yytext, ','); if (comma - yytext == strlen(yytext) - 2) // if comma is followed by the closing curly bracket // (example: {2,}) set high bound value to maximum. hi_bound = RE_MAX_RANGE; else hi_bound = atoi(comma + 1); if (hi_bound > RE_MAX_RANGE) { yyerror(yyscanner, lex_env, "repeat interval too large"); yyterminate(); } if (hi_bound < lo_bound || hi_bound < 0 || lo_bound < 0) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } if (hi_bound == 0 && lo_bound == 0) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } yylval->range = (hi_bound << 16) | lo_bound; return _RANGE_; } \{{digit}+\} { // Example: {10} int value = atoi(yytext + 1); // atoi can return a negative value if the input string represents a number // too large to fit in an integer. if (value > RE_MAX_RANGE || value < 0) { yyerror(yyscanner, lex_env, "repeat interval too large"); yyterminate(); } if (value == 0) { yyerror(yyscanner, lex_env, "bad repeat interval"); yyterminate(); } yylval->range = (value << 16) | value; return _RANGE_; } \[\^ { // Start of a negated character class. Example: [^abcd] BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = true; } \[\^\] { // Start of character negated class containing a ]. // Example: [^]abc] this must be interpreted as a class // not matching ], a, b, nor c BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = true; LEX_ENV->re_class.bitmap[']' / 8] |= 1 << ']' % 8; } \[\] { // Start of character class containing a ]. // Example: []abc] this must be interpreted as a class // matching ], a, b, or c. BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = false; LEX_ENV->re_class.bitmap[']' / 8] |= 1 << ']' % 8; } \[ { // Start of character class. Example: [abcd] BEGIN(char_class); memset(LEX_ENV->re_class.bitmap, 0, 32); LEX_ENV->re_class.negated = false; } [^\\\[\(\)\|\$\.\^\+\*\?] { // Any non-special character is passed as a CHAR token to the scanner. yylval->integer = yytext[0]; return _CHAR_; } \\w { return _WORD_CHAR_; } \\W { return _NON_WORD_CHAR_; } \\s { return _SPACE_; } \\S { return _NON_SPACE_; } \\d { return _DIGIT_; } \\D { return _NON_DIGIT_; } \\b { return _WORD_BOUNDARY_; } \\B { return _NON_WORD_BOUNDARY_; } \\{digit}+ { yyerror(yyscanner, lex_env, "backreferences are not allowed"); yyterminate(); } \\ { uint8_t c; if (read_escaped_char(yyscanner, &c)) { yylval->integer = c; return _CHAR_; } else { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } } \] { // End of character class. yylval->re_class = (RE_CLASS*) yr_malloc(sizeof(RE_CLASS)); memcpy(yylval->re_class->bitmap, LEX_ENV->re_class.bitmap, 32); yylval->re_class->negated = LEX_ENV->re_class.negated; BEGIN(INITIAL); return _CLASS_; } (\\x{hex_digit}{2}|\\.|[^\\])\-[^]] { // A range inside a character class. // [abc0-9] // ^- matching here uint16_t c; uint8_t start = yytext[0]; uint8_t end = yytext[2]; if (start == '\\') { if (!escaped_char_value(yytext, &start)) { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } if (yytext[1] == 'x') end = yytext[5]; else end = yytext[3]; } if (end == '\\') { if (!read_escaped_char(yyscanner, &end)) { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } } if (end < start) { yyerror(yyscanner, lex_env, "bad character range"); yyterminate(); } for (c = start; c <= end; c++) { LEX_ENV->re_class.bitmap[c / 8] |= 1 << c % 8; } } \\w { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= word_chars[i]; } \\W { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= ~word_chars[i]; } \\s { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= space_chars[i]; } \\S { int i; for (i = 0; i < 32; i++) LEX_ENV->re_class.bitmap[i] |= ~space_chars[i]; } \\d { char c; for (c = '0'; c <= '9'; c++) LEX_ENV->re_class.bitmap[c / 8] |= 1 << c % 8; } \\D { int i; for (i = 0; i < 32; i++) { // digits 0-7 are in the sixth byte of the vector, let that byte alone if (i == 6) continue; // digits 8 and 9 are the lowest two bits in the seventh byte of the // vector, let those bits alone. if (i == 7) LEX_ENV->re_class.bitmap[i] |= 0xFC; else LEX_ENV->re_class.bitmap[i] = 0xFF; } } \\ { uint8_t c; if (read_escaped_char(yyscanner, &c)) { LEX_ENV->re_class.bitmap[c / 8] |= 1 << c % 8; } else { yyerror(yyscanner, lex_env, "illegal escape sequence"); yyterminate(); } } . { if (yytext[0] >= 32 && yytext[0] < 127) { // A character class (i.e: [0-9a-f]) is represented by a 256-bits vector, // here we set to 1 the vector's bit corresponding to the input character. LEX_ENV->re_class.bitmap[yytext[0] / 8] |= 1 << yytext[0] % 8; } else { yyerror(yyscanner, lex_env, "non-ascii character"); yyterminate(); } } <> { // End of regexp reached while scanning a character class. yyerror(yyscanner, lex_env, "missing terminating ] for character class"); yyterminate(); } . { if (yytext[0] >= 32 && yytext[0] < 127) { return yytext[0]; } else { yyerror(yyscanner, lex_env, "non-ascii character"); yyterminate(); } } <> { yyterminate(); } %% int escaped_char_value( char* text, uint8_t* value) { unsigned int hex_value; char hex[3]; assert(text[0] == '\\'); switch(text[1]) { case 'x': if (!isxdigit(text[2]) || !isxdigit(text[3])) return 0; hex[0] = text[2]; hex[1] = text[3]; hex[2] = '\0'; sscanf(hex, "%x", &hex_value); *value = (uint8_t) hex_value; break; case 'n': *value = '\n'; break; case 't': *value = '\t'; break; case 'r': *value = '\r'; break; case 'f': *value = '\f'; break; case 'a': *value = '\a'; break; default: *value = text[1]; } return 1; } #ifdef __cplusplus #define RE_YY_INPUT yyinput #else #define RE_YY_INPUT input #endif int read_escaped_char( yyscan_t yyscanner, uint8_t* escaped_char) { char text[4] = {0, 0, 0, 0}; text[0] = '\\'; text[1] = RE_YY_INPUT(yyscanner); if (text[1] == EOF || text[1] == 0) return 0; if (text[1] == 'x') { text[2] = RE_YY_INPUT(yyscanner); if (text[2] == EOF || text[2] == 0) return 0; text[3] = RE_YY_INPUT(yyscanner); if (text[3] == EOF || text[3] == 0) return 0; } return escaped_char_value(text, escaped_char); } // // yyfatal (actually named re_yyfatal because of the '%option prefix="re_yy"' // directive) is called when a fatal error occurs in the parser. When this // happens we are deep inside the parsing logic generated by flex/bison and // the only way to exit gracefully from there is using setjmp/longjmp. // void yyfatal( yyscan_t yyscanner, const char *error_message) { jmp_buf* recovery_trampoline = (jmp_buf*) yr_thread_storage_get_value( &yr_yyfatal_trampoline_tls); longjmp(*recovery_trampoline, 1); } void yyerror( yyscan_t yyscanner, RE_LEX_ENVIRONMENT* lex_env, const char *error_message) { // if lex_env->last_error was set to some error code before // don't overwrite it, we are interested in the first error, not in // subsequent errors like "syntax error, unexpected $end" caused by // early parser termination. if (lex_env->last_error == ERROR_SUCCESS) { lex_env->last_error = ERROR_INVALID_REGULAR_EXPRESSION; strlcpy( lex_env->last_error_message, error_message, sizeof(lex_env->last_error_message)); } } int yr_parse_re_string( const char* re_string, RE_AST** re_ast, RE_ERROR* error) { yyscan_t yyscanner; jmp_buf recovery_trampoline; RE_LEX_ENVIRONMENT lex_env; lex_env.last_error = ERROR_SUCCESS; lex_env.last_error_message[0] = '\0'; yr_thread_storage_set_value( &yr_yyfatal_trampoline_tls, &recovery_trampoline); // setjmp returns a non-zero value only when we are returning to this // point via a call to longjmp to the recovery trampoline. if (setjmp(recovery_trampoline) != 0) return ERROR_INTERNAL_FATAL_ERROR; FAIL_ON_ERROR(yr_re_ast_create(re_ast)); yylex_init(&yyscanner); yyset_extra(*re_ast, yyscanner); yy_scan_string(re_string, yyscanner); yyparse(yyscanner, &lex_env); yylex_destroy(yyscanner); if (lex_env.last_error != ERROR_SUCCESS) { yr_re_ast_destroy(*re_ast); *re_ast = NULL; strlcpy( error->message, lex_env.last_error_message, sizeof(error->message)); return lex_env.last_error; } return ERROR_SUCCESS; } yara-4.1.3/libyara/rules.c000066400000000000000000000316611413423160300154000ustar00rootroot00000000000000/* Copyright (c) 2013. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include YR_API int yr_rules_define_integer_variable( YR_RULES* rules, const char* identifier, int64_t value) { YR_EXTERNAL_VARIABLE* external; if (identifier == NULL) return ERROR_INVALID_ARGUMENT; external = rules->ext_vars_table; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { if (strcmp(external->identifier, identifier) == 0) { if (external->type != EXTERNAL_VARIABLE_TYPE_INTEGER) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; external->value.i = value; return ERROR_SUCCESS; } external++; } return ERROR_INVALID_ARGUMENT; } YR_API int yr_rules_define_boolean_variable( YR_RULES* rules, const char* identifier, int value) { YR_EXTERNAL_VARIABLE* external; if (identifier == NULL) return ERROR_INVALID_ARGUMENT; external = rules->ext_vars_table; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { if (strcmp(external->identifier, identifier) == 0) { if (external->type != EXTERNAL_VARIABLE_TYPE_BOOLEAN) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; external->value.i = value; return ERROR_SUCCESS; } external++; } return ERROR_INVALID_ARGUMENT; } YR_API int yr_rules_define_float_variable( YR_RULES* rules, const char* identifier, double value) { YR_EXTERNAL_VARIABLE* external; if (identifier == NULL) return ERROR_INVALID_ARGUMENT; external = rules->ext_vars_table; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { if (strcmp(external->identifier, identifier) == 0) { if (external->type != EXTERNAL_VARIABLE_TYPE_FLOAT) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; external->value.f = value; return ERROR_SUCCESS; } external++; } return ERROR_INVALID_ARGUMENT; } YR_API int yr_rules_define_string_variable( YR_RULES* rules, const char* identifier, const char* value) { YR_EXTERNAL_VARIABLE* external; if (identifier == NULL || value == NULL) return ERROR_INVALID_ARGUMENT; external = rules->ext_vars_table; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { if (strcmp(external->identifier, identifier) == 0) { if (external->type != EXTERNAL_VARIABLE_TYPE_STRING && external->type != EXTERNAL_VARIABLE_TYPE_MALLOC_STRING) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; if (external->type == EXTERNAL_VARIABLE_TYPE_MALLOC_STRING && external->value.s != NULL) { yr_free(external->value.s); } external->type = EXTERNAL_VARIABLE_TYPE_MALLOC_STRING; external->value.s = yr_strdup(value); if (external->value.s == NULL) return ERROR_INSUFFICIENT_MEMORY; else return ERROR_SUCCESS; } external++; } return ERROR_INVALID_ARGUMENT; } YR_API int yr_rules_scan_mem_blocks( YR_RULES* rules, YR_MEMORY_BLOCK_ITERATOR* iterator, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) { YR_SCANNER* scanner; int result; FAIL_ON_ERROR(yr_scanner_create(rules, &scanner)); yr_scanner_set_callback(scanner, callback, user_data); yr_scanner_set_timeout(scanner, timeout); yr_scanner_set_flags(scanner, flags); result = yr_scanner_scan_mem_blocks(scanner, iterator); yr_scanner_destroy(scanner); return result; } YR_API int yr_rules_scan_mem( YR_RULES* rules, const uint8_t* buffer, size_t buffer_size, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) { YR_DEBUG_FPRINTF( 2, stderr, "+ %s(buffer=%p buffer_size=%zu timeout=%d) {\n", __FUNCTION__, buffer, buffer_size, timeout); YR_SCANNER* scanner; int result = ERROR_INTERNAL_FATAL_ERROR; GOTO_EXIT_ON_ERROR(yr_scanner_create(rules, &scanner)); yr_scanner_set_callback(scanner, callback, user_data); yr_scanner_set_timeout(scanner, timeout); yr_scanner_set_flags(scanner, flags); result = yr_scanner_scan_mem(scanner, buffer, buffer_size); yr_scanner_destroy(scanner); _exit: YR_DEBUG_FPRINTF( 2, stderr, "" "} = %d AKA %s // %s()\n", result, yr_debug_error_as_string(result), __FUNCTION__); return result; } YR_API int yr_rules_scan_file( YR_RULES* rules, const char* filename, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) { YR_MAPPED_FILE mfile; int result = yr_filemap_map(filename, &mfile); if (result == ERROR_SUCCESS) { result = yr_rules_scan_mem( rules, mfile.data, mfile.size, flags, callback, user_data, timeout); yr_filemap_unmap(&mfile); } return result; } YR_API int yr_rules_scan_fd( YR_RULES* rules, YR_FILE_DESCRIPTOR fd, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) { YR_MAPPED_FILE mfile; int result = yr_filemap_map_fd(fd, 0, 0, &mfile); if (result == ERROR_SUCCESS) { result = yr_rules_scan_mem( rules, mfile.data, mfile.size, flags, callback, user_data, timeout); yr_filemap_unmap_fd(&mfile); } return result; } YR_API int yr_rules_scan_proc( YR_RULES* rules, int pid, int flags, YR_CALLBACK_FUNC callback, void* user_data, int timeout) { YR_DEBUG_FPRINTF( 2, stderr, "+ %s(pid=%d timeout=%d) {\n", __FUNCTION__, pid, timeout); YR_MEMORY_BLOCK_ITERATOR iterator; int result = yr_process_open_iterator(pid, &iterator); if (result == ERROR_SUCCESS) { result = yr_rules_scan_mem_blocks( rules, &iterator, flags | SCAN_FLAGS_PROCESS_MEMORY, callback, user_data, timeout); yr_process_close_iterator(&iterator); } YR_DEBUG_FPRINTF( 2, stderr, "} = %d AKA %s // %s()\n", result, yr_debug_error_as_string(result), __FUNCTION__); return result; } int yr_rules_from_arena(YR_ARENA* arena, YR_RULES** rules) { YR_RULES* new_rules = (YR_RULES*) yr_malloc(sizeof(YR_RULES)); if (new_rules == NULL) return ERROR_INSUFFICIENT_MEMORY; YR_SUMMARY* summary = (YR_SUMMARY*) yr_arena_get_ptr( arena, YR_SUMMARY_SECTION, 0); // Now YR_RULES relies on this arena, let's increment the arena's // reference count so that if the original owner of the arena calls // yr_arena_destroy the arena is not destroyed. yr_arena_acquire(arena); new_rules->arena = arena; new_rules->num_rules = summary->num_rules; new_rules->num_strings = summary->num_strings; new_rules->num_namespaces = summary->num_namespaces; new_rules->rules_table = yr_arena_get_ptr(arena, YR_RULES_TABLE, 0); new_rules->strings_table = yr_arena_get_ptr(arena, YR_STRINGS_TABLE, 0); new_rules->ext_vars_table = yr_arena_get_ptr( arena, YR_EXTERNAL_VARIABLES_TABLE, 0); new_rules->ac_transition_table = yr_arena_get_ptr( arena, YR_AC_TRANSITION_TABLE, 0); new_rules->ac_match_table = yr_arena_get_ptr( arena, YR_AC_STATE_MATCHES_TABLE, 0); new_rules->ac_match_pool = yr_arena_get_ptr( arena, YR_AC_STATE_MATCHES_POOL, 0); new_rules->code_start = yr_arena_get_ptr(arena, YR_CODE_SECTION, 0); *rules = new_rules; return ERROR_SUCCESS; } YR_API int yr_rules_load_stream(YR_STREAM* stream, YR_RULES** rules) { YR_ARENA* arena; // Load the arena's data the stream. We are the owners of the arena. FAIL_ON_ERROR(yr_arena_load_stream(stream, &arena)); // Create the YR_RULES object from the arena, this makes YR_RULES owner // of the arena too. FAIL_ON_ERROR(yr_rules_from_arena(arena, rules)); // Release our ownership so that YR_RULES is the single owner. This way the // arena is destroyed when YR_RULES is destroyed. yr_arena_release(arena); return ERROR_SUCCESS; } YR_API int yr_rules_load(const char* filename, YR_RULES** rules) { int result; YR_STREAM stream; FILE* fh = fopen(filename, "rb"); if (fh == NULL) return ERROR_COULD_NOT_OPEN_FILE; stream.user_data = fh; stream.read = (YR_STREAM_READ_FUNC) fread; result = yr_rules_load_stream(&stream, rules); fclose(fh); return result; } YR_API int yr_rules_save_stream(YR_RULES* rules, YR_STREAM* stream) { return yr_arena_save_stream(rules->arena, stream); } YR_API int yr_rules_save(YR_RULES* rules, const char* filename) { int result; YR_STREAM stream; FILE* fh = fopen(filename, "wb"); if (fh == NULL) return ERROR_COULD_NOT_OPEN_FILE; stream.user_data = fh; stream.write = (YR_STREAM_WRITE_FUNC) fwrite; result = yr_rules_save_stream(rules, &stream); fclose(fh); return result; } static int _uint32_cmp(const void* a, const void* b) { return (*(uint32_t*) a - *(uint32_t*) b); } YR_API int yr_rules_get_stats(YR_RULES* rules, YR_RULES_STATS* stats) { memset(stats, 0, sizeof(YR_RULES_STATS)); stats->ac_tables_size = yr_arena_get_current_offset( rules->arena, YR_AC_TRANSITION_TABLE) / sizeof(YR_AC_TRANSITION); uint32_t* match_list_lengths = (uint32_t*) yr_malloc( sizeof(uint32_t) * stats->ac_tables_size); if (match_list_lengths == NULL) return ERROR_INSUFFICIENT_MEMORY; stats->num_rules = rules->num_rules; stats->num_strings = rules->num_strings; float match_list_length_sum = 0; int c = 0; for (uint32_t i = 0; i < stats->ac_tables_size; i++) { int match_list_length = 0; if (rules->ac_match_table[i] != 0) { YR_AC_MATCH* m = &rules->ac_match_pool[rules->ac_match_table[i] - 1]; while (m != NULL) { match_list_length++; stats->ac_matches++; m = m->next; } } if (i == 0) stats->ac_root_match_list_length = match_list_length; match_list_length_sum += match_list_length; if (match_list_length > 0) { match_list_lengths[c] = match_list_length; c++; } } if (c == 0) { yr_free(match_list_lengths); return ERROR_SUCCESS; } // sort match_list_lengths in increasing order for computing percentiles. qsort(match_list_lengths, c, sizeof(match_list_lengths[0]), _uint32_cmp); for (int i = 0; i < 100; i++) { if (i < c) stats->top_ac_match_list_lengths[i] = match_list_lengths[c - i - 1]; else stats->top_ac_match_list_lengths[i] = 0; } stats->ac_average_match_list_length = match_list_length_sum / c; stats->ac_match_list_length_pctls[0] = match_list_lengths[0]; stats->ac_match_list_length_pctls[100] = match_list_lengths[c - 1]; for (int i = 1; i < 100; i++) stats->ac_match_list_length_pctls[i] = match_list_lengths[(c * i) / 100]; yr_free(match_list_lengths); return ERROR_SUCCESS; } YR_API int yr_rules_destroy(YR_RULES* rules) { YR_EXTERNAL_VARIABLE* external = rules->ext_vars_table; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { if (external->type == EXTERNAL_VARIABLE_TYPE_MALLOC_STRING) yr_free(external->value.s); external++; } yr_arena_release(rules->arena); yr_free(rules); return ERROR_SUCCESS; } YR_API void yr_rule_disable(YR_RULE* rule) { YR_STRING* string; rule->flags |= RULE_FLAGS_DISABLED; yr_rule_strings_foreach(rule, string) { string->flags |= STRING_FLAGS_DISABLED; } } YR_API void yr_rule_enable(YR_RULE* rule) { YR_STRING* string; rule->flags &= ~RULE_FLAGS_DISABLED; yr_rule_strings_foreach(rule, string) { string->flags &= ~STRING_FLAGS_DISABLED; } } yara-4.1.3/libyara/scan.c000066400000000000000000000666511413423160300152010ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct _CALLBACK_ARGS { YR_STRING* string; YR_SCAN_CONTEXT* context; const uint8_t* data; size_t data_size; uint64_t data_base; int forward_matches; int full_word; } CALLBACK_ARGS; static int _yr_scan_xor_compare( const uint8_t* data, size_t data_size, uint8_t* string, size_t string_length) { int result = 0; const uint8_t* s1 = data; const uint8_t* s2 = string; uint8_t k = 0; size_t i = 0; if (data_size < string_length) goto _exit; // Calculate the xor key to compare with. *s1 is the start of the string we // matched on and *s2 is the "plaintext" string, so *s1 ^ *s2 is the key to // every *s2 as we compare. k = *s1 ^ *s2; while (i < string_length && *s1++ == ((*s2++) ^ k)) i++; result = (int) ((i == string_length) ? i : 0); _exit:; YR_DEBUG_FPRINTF( 2, stderr, "- %s(data_size=%zu string_length=%zu) {} = %d\n", __FUNCTION__, data_size, string_length, result); return result; } static int _yr_scan_xor_wcompare( const uint8_t* data, size_t data_size, uint8_t* string, size_t string_length) { const uint8_t* s1 = data; const uint8_t* s2 = string; uint8_t k = 0; size_t i = 0; if (data_size < string_length * 2) return 0; // Calculate the xor key to compare with. *s1 is the start of the string we // matched on and *s2 is the "plaintext" string, so *s1 ^ *s2 is the key to // every *s2 as we compare. k = *s1 ^ *s2; while (i < string_length && *s1 == ((*s2) ^ k) && ((*(s1 + 1)) ^ k) == 0x00) { s1 += 2; s2++; i++; } return (int) ((i == string_length) ? i * 2 : 0); } static int _yr_scan_compare( const uint8_t* data, size_t data_size, uint8_t* string, size_t string_length) { const uint8_t* s1 = data; const uint8_t* s2 = string; size_t i = 0; if (data_size < string_length) return 0; while (i < string_length && *s1++ == *s2++) i++; return (int) ((i == string_length) ? i : 0); } static int _yr_scan_icompare( const uint8_t* data, size_t data_size, uint8_t* string, size_t string_length) { const uint8_t* s1 = data; const uint8_t* s2 = string; size_t i = 0; if (data_size < string_length) return 0; while (i < string_length && yr_lowercase[*s1++] == yr_lowercase[*s2++]) i++; return (int) ((i == string_length) ? i : 0); } static int _yr_scan_wcompare( const uint8_t* data, size_t data_size, uint8_t* string, size_t string_length) { int result = 0; const uint8_t* s1 = data; const uint8_t* s2 = string; size_t i = 0; if (data_size < string_length * 2) goto _exit; while (i < string_length && *s1 == *s2 && *(s1 + 1) == 0x00) { s1 += 2; s2++; i++; } result = (int) ((i == string_length) ? i * 2 : 0); _exit:; YR_DEBUG_FPRINTF( 2, stderr, "- %s(data_size=%zu string_length=%zu) {} = %d\n", __FUNCTION__, data_size, string_length, result); return result; } static int _yr_scan_wicompare( const uint8_t* data, size_t data_size, uint8_t* string, size_t string_length) { int result = 0; const uint8_t* s1 = data; const uint8_t* s2 = string; size_t i = 0; if (data_size < string_length * 2) goto _exit; while (i < string_length && yr_lowercase[*s1] == yr_lowercase[*s2] && *(s1 + 1) == 0x00) { s1 += 2; s2++; i++; } result = (int) ((i == string_length) ? i * 2 : 0); _exit:; YR_DEBUG_FPRINTF( 2, stderr, "- %s(data_size=%zu string_length=%zu) {} = %d\n", __FUNCTION__, data_size, string_length, result); return result; } static void _yr_scan_update_match_chain_length( YR_SCAN_CONTEXT* context, YR_STRING* string, YR_MATCH* match_to_update, int chain_length) { YR_MATCH* match; if (match_to_update->chain_length == chain_length) return; match_to_update->chain_length = chain_length; if (string->chained_to == NULL) return; match = context->unconfirmed_matches[string->chained_to->idx].head; while (match != NULL) { int64_t ending_offset = match->offset + match->match_length; if (ending_offset + string->chain_gap_max >= match_to_update->offset && ending_offset + string->chain_gap_min <= match_to_update->offset) { _yr_scan_update_match_chain_length( context, string->chained_to, match, chain_length + 1); } match = match->next; } } static int _yr_scan_add_match_to_list( YR_MATCH* match, YR_MATCHES* matches_list, int replace_if_exists) { int result = ERROR_SUCCESS; #if YR_DEBUG_VERBOSITY > 0 int32_t count_orig = matches_list->count; #endif YR_MATCH* insertion_point = matches_list->tail; if (matches_list->count == YR_MAX_STRING_MATCHES) { result = ERROR_TOO_MANY_MATCHES; goto _exit; } while (insertion_point != NULL) { if ((match->base + match->offset) == (insertion_point->base + insertion_point->offset)) { if (replace_if_exists) { insertion_point->match_length = match->match_length; insertion_point->data_length = match->data_length; insertion_point->data = match->data; } goto _exit; // return ERROR_SUCCESS } if ((match->base + match->offset) > (insertion_point->base + insertion_point->offset)) break; insertion_point = insertion_point->prev; } match->prev = insertion_point; if (insertion_point != NULL) { match->next = insertion_point->next; insertion_point->next = match; } else { match->next = matches_list->head; matches_list->head = match; } matches_list->count++; if (match->next != NULL) match->next->prev = match; else matches_list->tail = match; _exit:; YR_DEBUG_FPRINTF( 2, stderr, "- %s(replace_if_exists=%d) {} = %d //" " match->base=0x%" PRIx64 " match->offset=%" PRIi64 " matches_list->count=%u += %u\n", __FUNCTION__, replace_if_exists, result, match->base, match->offset, count_orig, matches_list->count - count_orig); return result; } static void _yr_scan_remove_match_from_list( YR_MATCH* match, YR_MATCHES* matches_list) { if (match->prev != NULL) match->prev->next = match->next; if (match->next != NULL) match->next->prev = match->prev; if (matches_list->head == match) matches_list->head = match->next; if (matches_list->tail == match) matches_list->tail = match->prev; matches_list->count--; match->next = NULL; match->prev = NULL; } // // _yr_scan_verify_chained_string_match // // Given a string that is part of a string chain and is matching at some // point in the scanned data, this function determines if the whole string // chain is also matching. For example, if the string S was splitted and // converted in a chain S1 <- S2 <- S3 (see yr_re_ast_split_at_chaining_point), // and a match for S3 was found, this functions finds out if there are matches // for S1 and S2 that together with the match found for S3 conform a match for // the whole S. // // Notice that this function operates in a non-greedy fashion. Matches found // for S will be the shortest possible ones. // static int _yr_scan_verify_chained_string_match( YR_STRING* matching_string, YR_SCAN_CONTEXT* context, const uint8_t* match_data, uint64_t match_base, uint64_t match_offset, int32_t match_length) { YR_DEBUG_FPRINTF( 2, stderr, "- %s (match_data=%p match_base=%" PRIx64 " match_offset=0x%" PRIx64 " match_length=%'d) {} \n", __FUNCTION__, match_data, match_base, match_offset, match_length); YR_STRING* string; YR_MATCH* match; YR_MATCH* next_match; YR_MATCH* new_match; uint64_t lowest_offset; uint64_t ending_offset; int32_t full_chain_length; bool add_match = false; if (matching_string->chained_to == NULL) { // The matching string is the head of the chain, this match should be // added to the list of unconfirmed matches. The match will remain // unconfirmed until all the strings in the chain are found with the // correct distances between them. add_match = true; } else { // If some unconfirmed match exists, the lowest possible offset where the // whole string chain can match is the offset of the first string in the // list of unconfirmed matches. Unconfirmed matches are sorted in ascending // offset order. If no unconfirmed match exists, the lowest possible offset // is the offset of the current match. match = context->unconfirmed_matches[matching_string->idx].head; if (match != NULL) lowest_offset = match->offset; else lowest_offset = match_offset; // Iterate over the list of unconfirmed matches for the string that // precedes the currently matching string. If we have a string chain like: // S1 <- S2 <- S3, and we just found a match for S2, we are iterating the // list of unconfirmed matches of S1. match = context->unconfirmed_matches[matching_string->chained_to->idx].head; while (match != NULL) { // Store match->next so that we can use it later for advancing in the // list, if _yr_scan_remove_match_from_list is called, match->next is // set to NULL, that's why we store its current value before that happens. next_match = match->next; // The unconfirmed match starts at match->offset and finishes at // ending_offset. ending_offset = match->offset + match->match_length; if (ending_offset + matching_string->chain_gap_max < lowest_offset) { // If the current match is too far away from the unconfirmed match, // remove the unconfirmed match from the list because it has been // negatively confirmed (i.e: we can be sure that this unconfirmed // match can't be an actual match) _yr_scan_remove_match_from_list( match, &context->unconfirmed_matches[matching_string->chained_to->idx]); } else if ( ending_offset + matching_string->chain_gap_max >= match_offset && ending_offset + matching_string->chain_gap_min <= match_offset) { // If the distance between the end of the unconfirmed match and the // start of the current match is within the range specified in the // regexp or hex string, this could be an actual match. add_match = true; break; } match = next_match; } } if (add_match) { uint32_t max_match_data; FAIL_ON_ERROR( yr_get_configuration(YR_CONFIG_MAX_MATCH_DATA, &max_match_data)) if (STRING_IS_CHAIN_TAIL(matching_string)) { // The matching string is the tail of the string chain. It must be // chained to some other string. assert(matching_string->chained_to != NULL); // Iterate over the list of unconfirmed matches of the preceding string // in the chain and update the chain_length field for each of them. This // is a recursive operation that will update the chain_length field for // every unconfirmed match in all the strings in the chain up to the head // of the chain. match = context->unconfirmed_matches[matching_string->chained_to->idx].head; while (match != NULL) { ending_offset = match->offset + match->match_length; if (ending_offset + matching_string->chain_gap_max >= match_offset && ending_offset + matching_string->chain_gap_min <= match_offset) { _yr_scan_update_match_chain_length( context, matching_string->chained_to, match, 1); } match = match->next; } full_chain_length = 0; string = matching_string; while (string->chained_to != NULL) { full_chain_length++; string = string->chained_to; } // "string" points now to the head of the strings chain. match = context->unconfirmed_matches[string->idx].head; // Iterate over the list of unconfirmed matches of the head of the chain, // and move to the list of confirmed matches those with a chain_length // equal to full_chain_length, which means that the whole chain has been // confirmed to match. while (match != NULL) { next_match = match->next; if (match->chain_length == full_chain_length) { _yr_scan_remove_match_from_list( match, &context->unconfirmed_matches[string->idx]); match->match_length = (int32_t)( match_offset - match->offset + match_length); match->data_length = yr_min( match->match_length, (int32_t) max_match_data); match->data = yr_notebook_alloc( context->matches_notebook, match->data_length); if (match->data == NULL) return ERROR_INSUFFICIENT_MEMORY; memcpy( (void*) match->data, match_data - match_offset + match->offset, match->data_length); FAIL_ON_ERROR(_yr_scan_add_match_to_list( match, &context->matches[string->idx], false)); } match = next_match; } } else // It's a part of a chain, but not the tail. { new_match = yr_notebook_alloc( context->matches_notebook, sizeof(YR_MATCH)); if (new_match == NULL) return ERROR_INSUFFICIENT_MEMORY; new_match->base = match_base; new_match->offset = match_offset; new_match->match_length = match_length; new_match->chain_length = 0; new_match->prev = NULL; new_match->next = NULL; new_match->is_private = STRING_IS_PRIVATE(matching_string); // A copy of the matching data is written to the matches_arena, the // amount of data copies is limited by YR_CONFIG_MAX_MATCH_DATA. new_match->data_length = yr_min(match_length, (int32_t) max_match_data); if (new_match->data_length > 0) { new_match->data = yr_notebook_alloc( context->matches_notebook, new_match->data_length); if (new_match->data == NULL) return ERROR_INSUFFICIENT_MEMORY; memcpy((void*) new_match->data, match_data, new_match->data_length); } else { new_match->data = NULL; } // Add the match to the list of unconfirmed matches because the string // is part of a chain but not its tail, so we can't be sure the this is // an actual match until finding the remaining parts of the chain. FAIL_ON_ERROR(_yr_scan_add_match_to_list( new_match, &context->unconfirmed_matches[matching_string->idx], false)); } } return ERROR_SUCCESS; } static int _yr_scan_match_callback( const uint8_t* match_data, int32_t match_length, int flags, void* args) { CALLBACK_ARGS* callback_args = (CALLBACK_ARGS*) args; YR_STRING* string = callback_args->string; YR_MATCH* new_match; int result = ERROR_SUCCESS; size_t match_offset = match_data - callback_args->data; YR_DEBUG_FPRINTF( 2, stderr, "+ %s(match_data=%p match_length=%d) { //" " match_offset=%" PRId64 " args->data=%p args->string.length=%u" " args->data_base=0x%" PRIx64 " args->data_size=%zu" " args->forward_matches=%'u\n", __FUNCTION__, match_data, match_length, match_offset, callback_args->data, callback_args->string->length, callback_args->data_base, callback_args->data_size, callback_args->forward_matches); // total match length is the sum of backward and forward matches. match_length += callback_args->forward_matches; // make sure that match fits into the data. assert(match_offset + match_length <= callback_args->data_size); if (callback_args->full_word) { if (flags & RE_FLAGS_WIDE) { if (match_offset >= 2 && *(match_data - 1) == 0 && yr_isalnum(match_data - 2)) goto _exit; // return ERROR_SUCCESS; if (match_offset + match_length + 1 < callback_args->data_size && *(match_data + match_length + 1) == 0 && yr_isalnum(match_data + match_length)) goto _exit; // return ERROR_SUCCESS; } else { if (match_offset >= 1 && yr_isalnum(match_data - 1)) goto _exit; // return ERROR_SUCCESS; if (match_offset + match_length < callback_args->data_size && yr_isalnum(match_data + match_length)) goto _exit; // return ERROR_SUCCESS; } } if (STRING_IS_CHAIN_PART(string)) { result = _yr_scan_verify_chained_string_match( string, callback_args->context, match_data, callback_args->data_base, match_offset, match_length); } else { uint32_t max_match_data; FAIL_ON_ERROR( yr_get_configuration(YR_CONFIG_MAX_MATCH_DATA, &max_match_data)); new_match = yr_notebook_alloc( callback_args->context->matches_notebook, sizeof(YR_MATCH)); if (new_match == NULL) { result = ERROR_INSUFFICIENT_MEMORY; goto _exit; } new_match->data_length = yr_min(match_length, (int32_t) max_match_data); if (new_match->data_length > 0) { new_match->data = yr_notebook_alloc( callback_args->context->matches_notebook, new_match->data_length); if (new_match->data == NULL) { result = ERROR_INSUFFICIENT_MEMORY; goto _exit; } memcpy((void*) new_match->data, match_data, new_match->data_length); } else { new_match->data = NULL; } if (result == ERROR_SUCCESS) { new_match->base = callback_args->data_base; new_match->offset = match_offset; new_match->match_length = match_length; new_match->prev = NULL; new_match->next = NULL; new_match->is_private = STRING_IS_PRIVATE(string); FAIL_ON_ERROR(_yr_scan_add_match_to_list( new_match, &callback_args->context->matches[string->idx], STRING_IS_GREEDY_REGEXP(string))); } } _exit:; YR_DEBUG_FPRINTF(2, stderr, "} = %d // %s()\n", result, __FUNCTION__); return result; } typedef int (*RE_EXEC_FUNC)( YR_SCAN_CONTEXT* context, const uint8_t* code, const uint8_t* input, size_t input_forwards_size, size_t input_backwards_size, int flags, RE_MATCH_CALLBACK_FUNC callback, void* callback_args, int* matches); static int _yr_scan_verify_re_match( YR_SCAN_CONTEXT* context, YR_AC_MATCH* ac_match, const uint8_t* data, size_t data_size, uint64_t data_base, size_t offset) { YR_DEBUG_FPRINTF( 2, stderr, "- %s(data=%p data_size=%zu data_base=0x%" PRIx64 " offset=%zu) {}\n", __FUNCTION__, data, data_size, data_base, offset); CALLBACK_ARGS callback_args; RE_EXEC_FUNC exec; int forward_matches = -1; int backward_matches = -1; int flags = 0; if (STRING_IS_GREEDY_REGEXP(ac_match->string)) flags |= RE_FLAGS_GREEDY; if (STRING_IS_NO_CASE(ac_match->string)) flags |= RE_FLAGS_NO_CASE; if (STRING_IS_DOT_ALL(ac_match->string)) flags |= RE_FLAGS_DOT_ALL; if (STRING_IS_FAST_REGEXP(ac_match->string)) exec = yr_re_fast_exec; else exec = yr_re_exec; if (STRING_IS_ASCII(ac_match->string) || STRING_IS_BASE64(ac_match->string) || STRING_IS_BASE64_WIDE(ac_match->string)) { FAIL_ON_ERROR(exec( context, ac_match->forward_code, data + offset, data_size - offset, offset, flags, NULL, NULL, &forward_matches)); } if ((forward_matches == -1) && (STRING_IS_WIDE(ac_match->string) && !(STRING_IS_BASE64(ac_match->string) || STRING_IS_BASE64_WIDE(ac_match->string)))) { flags |= RE_FLAGS_WIDE; FAIL_ON_ERROR(exec( context, ac_match->forward_code, data + offset, data_size - offset, offset, flags, NULL, NULL, &forward_matches)); } if (forward_matches == -1) return ERROR_SUCCESS; if (forward_matches == 0 && ac_match->backward_code == NULL) return ERROR_SUCCESS; callback_args.string = ac_match->string; callback_args.context = context; callback_args.data = data; callback_args.data_size = data_size; callback_args.data_base = data_base; callback_args.forward_matches = forward_matches; callback_args.full_word = STRING_IS_FULL_WORD(ac_match->string); if (ac_match->backward_code != NULL) { FAIL_ON_ERROR(exec( context, ac_match->backward_code, data + offset, data_size - offset, offset, flags | RE_FLAGS_BACKWARDS | RE_FLAGS_EXHAUSTIVE, _yr_scan_match_callback, (void*) &callback_args, &backward_matches)); } else { FAIL_ON_ERROR( _yr_scan_match_callback(data + offset, 0, flags, &callback_args)); } return ERROR_SUCCESS; } static int _yr_scan_verify_literal_match( YR_SCAN_CONTEXT* context, YR_AC_MATCH* ac_match, const uint8_t* data, size_t data_size, uint64_t data_base, size_t offset) { YR_DEBUG_FPRINTF( 2, stderr, "- %s(data=%p data_size=%zu data_base=0x%" PRIx64 " offset=%zu) {}\n", __FUNCTION__, data, data_size, data_base, offset); int flags = 0; int forward_matches = 0; CALLBACK_ARGS callback_args; YR_STRING* string = ac_match->string; if (STRING_FITS_IN_ATOM(string)) { forward_matches = ac_match->backtrack; } else if (STRING_IS_NO_CASE(string)) { if (STRING_IS_ASCII(string)) { forward_matches = _yr_scan_icompare( data + offset, data_size - offset, string->string, string->length); } if (STRING_IS_WIDE(string) && forward_matches == 0) { forward_matches = _yr_scan_wicompare( data + offset, data_size - offset, string->string, string->length); } } else { if (STRING_IS_ASCII(string)) { forward_matches = _yr_scan_compare( data + offset, data_size - offset, string->string, string->length); } if (STRING_IS_WIDE(string) && forward_matches == 0) { forward_matches = _yr_scan_wcompare( data + offset, data_size - offset, string->string, string->length); } if (STRING_IS_XOR(string) && forward_matches == 0) { if (STRING_IS_WIDE(string)) { forward_matches = _yr_scan_xor_wcompare( data + offset, data_size - offset, string->string, string->length); } if (forward_matches == 0) { forward_matches = _yr_scan_xor_compare( data + offset, data_size - offset, string->string, string->length); } } } if (forward_matches == 0) return ERROR_SUCCESS; if (forward_matches == string->length * 2) flags |= RE_FLAGS_WIDE; if (STRING_IS_NO_CASE(string)) flags |= RE_FLAGS_NO_CASE; callback_args.context = context; callback_args.string = string; callback_args.data = data; callback_args.data_size = data_size; callback_args.data_base = data_base; callback_args.forward_matches = forward_matches; callback_args.full_word = STRING_IS_FULL_WORD(string); FAIL_ON_ERROR( _yr_scan_match_callback(data + offset, 0, flags, &callback_args)); return ERROR_SUCCESS; } int yr_scan_verify_match( YR_SCAN_CONTEXT* context, YR_AC_MATCH* ac_match, const uint8_t* data, size_t data_size, uint64_t data_base, size_t offset) { YR_DEBUG_FPRINTF( 2, stderr, "- %s(data=%p data_size=%zu data_base=0x%" PRIx64 " offset=%zu) {}\n", __FUNCTION__, data, data_size, data_base, offset); YR_STRING* string = ac_match->string; YR_CALLBACK_FUNC callback = context->callback; int result; if (data_size - offset <= 0) return ERROR_SUCCESS; if (yr_bitmask_is_set(context->strings_temp_disabled, string->idx)) return ERROR_SUCCESS; if (context->flags & SCAN_FLAGS_FAST_MODE && STRING_IS_SINGLE_MATCH(string) && context->matches[string->idx].head != NULL) return ERROR_SUCCESS; if (STRING_IS_FIXED_OFFSET(string) && string->fixed_offset != data_base + offset) return ERROR_SUCCESS; #ifdef YR_PROFILING_ENABLED uint64_t start_time; bool sample = context->profiling_info[string->rule_idx].atom_matches % YR_MATCH_VERIFICATION_PROFILING_RATE == 0; if (sample) start_time = yr_stopwatch_elapsed_ns(&context->stopwatch); #endif if (STRING_IS_LITERAL(string)) { result = _yr_scan_verify_literal_match( context, ac_match, data, data_size, data_base, offset); } else { result = _yr_scan_verify_re_match( context, ac_match, data, data_size, data_base, offset); } // If _yr_scan_verify_literal_match or _yr_scan_verify_re_match return // ERROR_TOO_MANY_MATCHES call the callback with CALLBACK_MSG_TOO_MANY_MATCHES // in order to ask what to do. If the callback returns CALLBACK_CONTINUE // this error is ignored, if not, the error is propagated to the caller. if (result == ERROR_TOO_MANY_MATCHES) { result = callback( context, CALLBACK_MSG_TOO_MANY_MATCHES, (void*) string, context->user_data); switch (result) { case CALLBACK_CONTINUE: yr_bitmask_set(context->strings_temp_disabled, string->idx); result = ERROR_SUCCESS; break; default: result = ERROR_TOO_MANY_MATCHES; break; } } #ifdef YR_PROFILING_ENABLED if (sample) { uint64_t finish_time = yr_stopwatch_elapsed_ns(&context->stopwatch); context->profiling_info[string->rule_idx].match_time += (finish_time - start_time); } context->profiling_info[string->rule_idx].atom_matches++; #endif if (result != ERROR_SUCCESS) context->last_error_string = string; return result; } yara-4.1.3/libyara/scanner.c000066400000000000000000000515611413423160300157000ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include "exception.h" static int _yr_scanner_scan_mem_block( YR_SCANNER* scanner, const uint8_t* block_data, YR_MEMORY_BLOCK* block) { YR_DEBUG_FPRINTF( 2, stderr, "+ %s(block_data=%p block->base=0x%" PRIx64 " block->size=%zu) {\n", __FUNCTION__, block_data, block->base, block->size); int result = ERROR_SUCCESS; YR_RULES* rules = scanner->rules; YR_AC_TRANSITION* transition_table = rules->ac_transition_table; uint32_t* match_table = rules->ac_match_table; YR_AC_MATCH* match; YR_AC_TRANSITION transition; size_t i = 0; uint32_t state = YR_AC_ROOT_STATE; uint16_t index; while (i < block->size) { if (i % 4096 == 0 && scanner->timeout > 0) { if (yr_stopwatch_elapsed_ns(&scanner->stopwatch) > scanner->timeout) { result = ERROR_SCAN_TIMEOUT; goto _exit; } } #if 2 == YR_DEBUG_VERBOSITY if (0 != state) YR_DEBUG_FPRINTF( 2, stderr, "- match_table[state=%u]=%'u i=%'ld " "block_data=%p block->base=0x%" PRIx64 " // %s()\n", state, match_table[state], i, block_data, block->base, __FUNCTION__); #endif if (match_table[state] != 0) { // If the entry corresponding to state N in the match table is zero, it // means that there's no match associated to the state. If it's non-zero, // its value is the 1-based index within ac_match_pool where the first // match resides. match = &rules->ac_match_pool[match_table[state] - 1]; while (match != NULL) { if (match->backtrack <= i) { GOTO_EXIT_ON_ERROR(yr_scan_verify_match( scanner, match, block_data, block->size, block->base, i - match->backtrack)); } match = match->next; } } index = block_data[i++] + 1; transition = transition_table[state + index]; while (YR_AC_INVALID_TRANSITION(transition, index)) { if (state != YR_AC_ROOT_STATE) { state = YR_AC_NEXT_STATE(transition_table[state]); transition = transition_table[state + index]; } else { transition = 0; break; } } state = YR_AC_NEXT_STATE(transition); } if (match_table[state] != 0) { match = &rules->ac_match_pool[match_table[state] - 1]; while (match != NULL) { if (match->backtrack <= i) { GOTO_EXIT_ON_ERROR(yr_scan_verify_match( scanner, match, block_data, block->size, block->base, i - match->backtrack)); } match = match->next; } } _exit: YR_DEBUG_FPRINTF( 2, stderr, "} = %d AKA %s // %s()\n", result, yr_debug_error_as_string(result), __FUNCTION__); return result; } static void _yr_scanner_clean_matches(YR_SCANNER* scanner) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {} \n", __FUNCTION__); memset( scanner->rule_matches_flags, 0, sizeof(YR_BITMASK) * YR_BITMASK_SIZE(scanner->rules->num_rules)); memset( scanner->ns_unsatisfied_flags, 0, sizeof(YR_BITMASK) * YR_BITMASK_SIZE(scanner->rules->num_namespaces)); memset( scanner->strings_temp_disabled, 0, sizeof(YR_BITMASK) * YR_BITMASK_SIZE(scanner->rules->num_strings)); memset(scanner->matches, 0, sizeof(YR_MATCHES) * scanner->rules->num_strings); memset( scanner->unconfirmed_matches, 0, sizeof(YR_MATCHES) * scanner->rules->num_strings); } YR_API int yr_scanner_create(YR_RULES* rules, YR_SCANNER** scanner) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {} \n", __FUNCTION__); YR_EXTERNAL_VARIABLE* external; YR_SCANNER* new_scanner; new_scanner = (YR_SCANNER*) yr_calloc(1, sizeof(YR_SCANNER)); if (new_scanner == NULL) return ERROR_INSUFFICIENT_MEMORY; FAIL_ON_ERROR_WITH_CLEANUP( yr_hash_table_create(64, &new_scanner->objects_table), yr_free(new_scanner)); new_scanner->rules = rules; new_scanner->entry_point = YR_UNDEFINED; new_scanner->file_size = YR_UNDEFINED; new_scanner->canary = rand(); // By default report both matching and non-matching rules. new_scanner->flags = SCAN_FLAGS_REPORT_RULES_MATCHING | SCAN_FLAGS_REPORT_RULES_NOT_MATCHING; new_scanner->rule_matches_flags = (YR_BITMASK*) yr_calloc( sizeof(YR_BITMASK), YR_BITMASK_SIZE(rules->num_rules)); new_scanner->ns_unsatisfied_flags = (YR_BITMASK*) yr_calloc( sizeof(YR_BITMASK), YR_BITMASK_SIZE(rules->num_namespaces)); new_scanner->strings_temp_disabled = (YR_BITMASK*) yr_calloc( sizeof(YR_BITMASK), YR_BITMASK_SIZE(rules->num_strings)); new_scanner->matches = (YR_MATCHES*) yr_calloc( rules->num_strings, sizeof(YR_MATCHES)); new_scanner->unconfirmed_matches = (YR_MATCHES*) yr_calloc( rules->num_strings, sizeof(YR_MATCHES)); #ifdef YR_PROFILING_ENABLED new_scanner->profiling_info = yr_calloc( rules->num_rules, sizeof(YR_PROFILING_INFO)); if (new_scanner->profiling_info == NULL) { yr_scanner_destroy(new_scanner); return ERROR_INSUFFICIENT_MEMORY; } #else new_scanner->profiling_info = NULL; #endif external = rules->ext_vars_table; while (!EXTERNAL_VARIABLE_IS_NULL(external)) { YR_OBJECT* object; FAIL_ON_ERROR_WITH_CLEANUP( yr_object_from_external_variable(external, &object), // cleanup yr_scanner_destroy(new_scanner)); FAIL_ON_ERROR_WITH_CLEANUP(yr_hash_table_add( new_scanner->objects_table, external->identifier, NULL, (void*) object), // cleanup yr_object_destroy(object); yr_scanner_destroy(new_scanner)); yr_object_set_canary(object, new_scanner->canary); external++; } *scanner = new_scanner; return ERROR_SUCCESS; } YR_API void yr_scanner_destroy(YR_SCANNER* scanner) { YR_DEBUG_FPRINTF(2, stderr, "- %s() {} \n", __FUNCTION__); RE_FIBER* fiber; RE_FIBER* next_fiber; fiber = scanner->re_fiber_pool.fibers.head; while (fiber != NULL) { next_fiber = fiber->next; yr_free(fiber); fiber = next_fiber; } if (scanner->objects_table != NULL) { yr_hash_table_destroy( scanner->objects_table, (YR_HASH_TABLE_FREE_VALUE_FUNC) yr_object_destroy); } #ifdef YR_PROFILING_ENABLED yr_free(scanner->profiling_info); #endif yr_free(scanner->rule_matches_flags); yr_free(scanner->ns_unsatisfied_flags); yr_free(scanner->strings_temp_disabled); yr_free(scanner->matches); yr_free(scanner->unconfirmed_matches); yr_free(scanner); } YR_API void yr_scanner_set_callback( YR_SCANNER* scanner, YR_CALLBACK_FUNC callback, void* user_data) { scanner->callback = callback; scanner->user_data = user_data; } YR_API void yr_scanner_set_timeout(YR_SCANNER* scanner, int timeout) { // Convert timeout from seconds to nanoseconds. scanner->timeout = timeout * 1000000000ULL; } YR_API void yr_scanner_set_flags(YR_SCANNER* scanner, int flags) { // For backward compatibility, if neither SCAN_FLAGS_REPORT_RULES_MATCHING // nor SCAN_FLAGS_REPORT_RULES_NOT_MATCHING are specified, both are assumed. if (!(flags & SCAN_FLAGS_REPORT_RULES_MATCHING) && !(flags & SCAN_FLAGS_REPORT_RULES_NOT_MATCHING)) { flags |= SCAN_FLAGS_REPORT_RULES_MATCHING | SCAN_FLAGS_REPORT_RULES_NOT_MATCHING; } scanner->flags = flags; } YR_API int yr_scanner_define_integer_variable( YR_SCANNER* scanner, const char* identifier, int64_t value) { YR_OBJECT* obj = (YR_OBJECT*) yr_hash_table_lookup( scanner->objects_table, identifier, NULL); if (obj == NULL) return ERROR_INVALID_ARGUMENT; if (obj->type != OBJECT_TYPE_INTEGER) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; return yr_object_set_integer(value, obj, NULL); } YR_API int yr_scanner_define_boolean_variable( YR_SCANNER* scanner, const char* identifier, int value) { return yr_scanner_define_integer_variable(scanner, identifier, value); } YR_API int yr_scanner_define_float_variable( YR_SCANNER* scanner, const char* identifier, double value) { YR_OBJECT* obj = (YR_OBJECT*) yr_hash_table_lookup( scanner->objects_table, identifier, NULL); if (obj == NULL) return ERROR_INVALID_ARGUMENT; if (obj->type != OBJECT_TYPE_FLOAT) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; return yr_object_set_float(value, obj, NULL); } YR_API int yr_scanner_define_string_variable( YR_SCANNER* scanner, const char* identifier, const char* value) { YR_OBJECT* obj = (YR_OBJECT*) yr_hash_table_lookup( scanner->objects_table, identifier, NULL); if (obj == NULL) return ERROR_INVALID_ARGUMENT; if (obj->type != OBJECT_TYPE_STRING) return ERROR_INVALID_EXTERNAL_VARIABLE_TYPE; return yr_object_set_string(value, strlen(value), obj, NULL); } YR_API int yr_scanner_scan_mem_blocks( YR_SCANNER* scanner, YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_DEBUG_FPRINTF(2, stderr, "+ %s() {\n", __FUNCTION__); YR_RULES* rules; YR_RULE* rule; YR_MEMORY_BLOCK* block; int i, result = ERROR_SUCCESS; if (scanner->callback == NULL) { result = ERROR_CALLBACK_REQUIRED; goto _exit; } scanner->iterator = iterator; rules = scanner->rules; if (iterator->last_error == ERROR_BLOCK_NOT_READY) { // The caller is invoking yr_scanner_scan_mem_blocks again because the // previous call returned ERROR_BLOCK_NOT_READY. block = iterator->next(iterator); } else { // Create the notebook that will hold the YR_MATCH structures representing // each match found. This notebook will also contain snippets of the // matching data (the "data" field in YR_MATCH points to the snippet // corresponding to the match). Each notebook's page can store up to 1024 // matches. uint32_t max_match_data; FAIL_ON_ERROR( yr_get_configuration(YR_CONFIG_MAX_MATCH_DATA, &max_match_data)); result = yr_notebook_create( 1024 * (sizeof(YR_MATCH) + max_match_data), &scanner->matches_notebook); if (result != ERROR_SUCCESS) goto _exit; yr_stopwatch_start(&scanner->stopwatch); block = iterator->first(iterator); } while (block != NULL) { const uint8_t* data = block->fetch_data(block); // fetch_data may fail and return NULL. if (data == NULL) { block = iterator->next(iterator); continue; } if (scanner->entry_point == YR_UNDEFINED) { YR_TRYCATCH( !(scanner->flags & SCAN_FLAGS_NO_TRYCATCH), { if (scanner->flags & SCAN_FLAGS_PROCESS_MEMORY) scanner->entry_point = yr_get_entry_point_address( data, block->size, block->base); else scanner->entry_point = yr_get_entry_point_offset( data, block->size); }, {}); } YR_TRYCATCH( !(scanner->flags & SCAN_FLAGS_NO_TRYCATCH), { result = _yr_scanner_scan_mem_block(scanner, data, block); }, { result = ERROR_COULD_NOT_MAP_FILE; }); if (result != ERROR_SUCCESS) goto _exit; block = iterator->next(iterator); } result = iterator->last_error; if (result != ERROR_SUCCESS) goto _exit; // If the iterator has a file_size function, ask the function for the file's // size, if not file size is undefined. if (iterator->file_size != NULL) scanner->file_size = iterator->file_size(iterator); else scanner->file_size = YR_UNDEFINED; YR_TRYCATCH( !(scanner->flags & SCAN_FLAGS_NO_TRYCATCH), { result = yr_execute_code(scanner); }, { result = ERROR_COULD_NOT_MAP_FILE; }); if (result != ERROR_SUCCESS) goto _exit; for (i = 0, rule = rules->rules_table; !RULE_IS_NULL(rule); i++, rule++) { int message = 0; if (yr_bitmask_is_set(scanner->rule_matches_flags, i) && yr_bitmask_is_not_set(scanner->ns_unsatisfied_flags, rule->ns->idx)) { if (scanner->flags & SCAN_FLAGS_REPORT_RULES_MATCHING) message = CALLBACK_MSG_RULE_MATCHING; } else { if (scanner->flags & SCAN_FLAGS_REPORT_RULES_NOT_MATCHING) message = CALLBACK_MSG_RULE_NOT_MATCHING; } if (message != 0 && !RULE_IS_PRIVATE(rule)) { switch (scanner->callback(scanner, message, rule, scanner->user_data)) { case CALLBACK_ABORT: result = ERROR_SUCCESS; goto _exit; case CALLBACK_ERROR: result = ERROR_CALLBACK_ERROR; goto _exit; } } } scanner->callback( scanner, CALLBACK_MSG_SCAN_FINISHED, NULL, scanner->user_data); _exit: // If error is ERROR_BLOCK_NOT_READY we don't clean the matches and don't // destroy the notebook yet. ERROR_BLOCK_NOT_READY is not a permament error, // the caller can still call this function again for a retry. if (result != ERROR_BLOCK_NOT_READY) { _yr_scanner_clean_matches(scanner); if (scanner->matches_notebook != NULL) { yr_notebook_destroy(scanner->matches_notebook); scanner->matches_notebook = NULL; } } YR_DEBUG_FPRINTF( 2, stderr, "} = %d AKA %s // %s()\n", result, yr_debug_error_as_string(result), __FUNCTION__); return result; } static YR_MEMORY_BLOCK* _yr_get_first_block(YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_MEMORY_BLOCK* result = (YR_MEMORY_BLOCK*) iterator->context; YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = %p // default iterator; single memory block, blocking\n", __FUNCTION__, result); return result; } static YR_MEMORY_BLOCK* _yr_get_next_block(YR_MEMORY_BLOCK_ITERATOR* iterator) { YR_MEMORY_BLOCK* result = NULL; YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = %p // default iterator; single memory block, blocking\n", __FUNCTION__, result); return result; } static uint64_t _yr_get_file_size(YR_MEMORY_BLOCK_ITERATOR* iterator) { uint64_t file_size = ((YR_MEMORY_BLOCK*) iterator->context)->size; YR_DEBUG_FPRINTF( 2, stderr, "- %s() {} = %" PRIu64 " // default iterator; single memory block, blocking\n", __FUNCTION__, file_size); return file_size; } static const uint8_t* _yr_fetch_block_data(YR_MEMORY_BLOCK* block) { return (const uint8_t*) block->context; } YR_API int yr_scanner_scan_mem( YR_SCANNER* scanner, const uint8_t* buffer, size_t buffer_size) { YR_DEBUG_FPRINTF( 2, stderr, "+ %s(buffer=%p buffer_size=%zu) {\n", __FUNCTION__, buffer, buffer_size); YR_MEMORY_BLOCK block; YR_MEMORY_BLOCK_ITERATOR iterator; block.size = buffer_size; block.base = 0; block.fetch_data = _yr_fetch_block_data; block.context = (void*) buffer; iterator.context = █ iterator.first = _yr_get_first_block; iterator.next = _yr_get_next_block; iterator.file_size = _yr_get_file_size; iterator.last_error = ERROR_SUCCESS; int result = yr_scanner_scan_mem_blocks(scanner, &iterator); YR_DEBUG_FPRINTF( 2, stderr, "} = %d AKA %s // %s()\n", result, yr_debug_error_as_string(result), __FUNCTION__); return result; } YR_API int yr_scanner_scan_file(YR_SCANNER* scanner, const char* filename) { YR_MAPPED_FILE mfile; int result = yr_filemap_map(filename, &mfile); if (result == ERROR_SUCCESS) { result = yr_scanner_scan_mem(scanner, mfile.data, mfile.size); yr_filemap_unmap(&mfile); } return result; } YR_API int yr_scanner_scan_fd(YR_SCANNER* scanner, YR_FILE_DESCRIPTOR fd) { YR_MAPPED_FILE mfile; int result = yr_filemap_map_fd(fd, 0, 0, &mfile); if (result == ERROR_SUCCESS) { result = yr_scanner_scan_mem(scanner, mfile.data, mfile.size); yr_filemap_unmap_fd(&mfile); } return result; } YR_API int yr_scanner_scan_proc(YR_SCANNER* scanner, int pid) { YR_MEMORY_BLOCK_ITERATOR iterator; int result = yr_process_open_iterator(pid, &iterator); if (result == ERROR_SUCCESS) { int prev_flags = scanner->flags; scanner->flags |= SCAN_FLAGS_PROCESS_MEMORY; result = yr_scanner_scan_mem_blocks(scanner, &iterator); scanner->flags = prev_flags; yr_process_close_iterator(&iterator); } return result; } YR_API YR_STRING* yr_scanner_last_error_string(YR_SCANNER* scanner) { return scanner->last_error_string; } YR_API YR_RULE* yr_scanner_last_error_rule(YR_SCANNER* scanner) { if (scanner->last_error_string == NULL) return NULL; return &scanner->rules->rules_table[scanner->last_error_string->rule_idx]; } static int sort_by_cost_desc( const struct YR_RULE_PROFILING_INFO* r1, const struct YR_RULE_PROFILING_INFO* r2) { if (r1->cost < r2->cost) return 1; if (r1->cost > r2->cost) return -1; return 0; } // // yr_scanner_get_profiling_info // // Returns a pointer to an array of YR_RULE_PROFILING_INFO structures with // information about the cost of each rule. The rules are sorted by cost // in descending order and the last item in the array has rule == NULL. // The caller is responsible for freeing the returned array by calling // yr_free. Calling this function only makes sense if YR_PROFILING_ENABLED // is defined, if not, the cost for each rule won't be computed, it will be // set to 0 for all rules. // YR_API YR_RULE_PROFILING_INFO* yr_scanner_get_profiling_info( YR_SCANNER* scanner) { YR_RULE_PROFILING_INFO* profiling_info = yr_malloc( (scanner->rules->num_rules + 1) * sizeof(YR_RULE_PROFILING_INFO)); if (profiling_info == NULL) return NULL; for (uint32_t i = 0; i < scanner->rules->num_rules; i++) { profiling_info[i].rule = &scanner->rules->rules_table[i]; #ifdef YR_PROFILING_ENABLED profiling_info[i].cost = scanner->profiling_info[i].exec_time + (scanner->profiling_info[i].atom_matches * scanner->profiling_info[i].match_time) / YR_MATCH_VERIFICATION_PROFILING_RATE; #else memset(&profiling_info[i], 0, sizeof(YR_RULE_PROFILING_INFO)); #endif } qsort( profiling_info, scanner->rules->num_rules, sizeof(YR_RULE_PROFILING_INFO), (int (*)(const void*, const void*)) sort_by_cost_desc); profiling_info[scanner->rules->num_rules].rule = NULL; profiling_info[scanner->rules->num_rules].cost = 0; return profiling_info; } YR_API void yr_scanner_reset_profiling_info(YR_SCANNER* scanner) { #ifdef YR_PROFILING_ENABLED memset( scanner->profiling_info, 0, scanner->rules->num_rules * sizeof(YR_PROFILING_INFO)); #endif } YR_API int yr_scanner_print_profiling_info(YR_SCANNER* scanner) { printf("\n===== PROFILING INFORMATION =====\n\n"); YR_RULE_PROFILING_INFO* info = yr_scanner_get_profiling_info(scanner); if (info == NULL) return ERROR_INSUFFICIENT_MEMORY; YR_RULE_PROFILING_INFO* rpi = info; while (rpi->rule != NULL) { printf( "%10" PRIu64 " %s:%s: \n", rpi->cost, rpi->rule->ns->name, rpi->rule->identifier); rpi++; } printf("\n=================================\n"); yr_free(info); return ERROR_SUCCESS; } yara-4.1.3/libyara/sizedstr.c000066400000000000000000000154301413423160300161110ustar00rootroot00000000000000/* Copyright (c) 2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include //////////////////////////////////////////////////////////////////////////////// // ss_compare returns: // 0 if s1 == s2 // -1 if s1 < s2 // 1 if s1 > s2 // int ss_compare(SIZED_STRING* s1, SIZED_STRING* s2) { size_t i = 0; while (s1->length > i && s2->length > i && s1->c_string[i] == s2->c_string[i]) { i++; } if (i == s1->length && i == s2->length) return 0; else if (i == s1->length) return -1; else if (i == s2->length) return 1; else if (s1->c_string[i] < s2->c_string[i]) return -1; else return 1; } //////////////////////////////////////////////////////////////////////////////// // ss_icompare is the case-insensitive version of ss_compare. // int ss_icompare(SIZED_STRING* s1, SIZED_STRING* s2) { size_t i = 0; while (s1->length > i && s2->length > i && yr_lowercase[(uint8_t) s1->c_string[i]] == yr_lowercase[(uint8_t) s2->c_string[i]]) { i++; } if (i == s1->length && i == s2->length) return 0; else if (i == s1->length) return -1; else if (i == s2->length) return 1; else if (s1->c_string[i] < s2->c_string[i]) return -1; else return 1; } //////////////////////////////////////////////////////////////////////////////// // ss_contains returns true if the sized string s1 contains s2. // bool ss_contains(SIZED_STRING* s1, SIZED_STRING* s2) { return memmem(s1->c_string, s1->length, s2->c_string, s2->length) != NULL; } //////////////////////////////////////////////////////////////////////////////// // ss_icontains is the case-insensitive version of ss_contains. // bool ss_icontains(SIZED_STRING* s1, SIZED_STRING* s2) { if (s1->length < s2->length) return false; for (uint32_t i = 0; i < s1->length - s2->length + 1; i++) { uint32_t j = 0; for (j = 0; j < s2->length; j++) if (yr_lowercase[(uint8_t) s1->c_string[i + j]] != yr_lowercase[(uint8_t) s2->c_string[j]]) break; if (j == s2->length) return true; } return false; } //////////////////////////////////////////////////////////////////////////////// // ss_startswith returns true if the sized string s1 starts with s2. // bool ss_startswith(SIZED_STRING* s1, SIZED_STRING* s2) { if (s1->length < s2->length) return false; for (uint32_t i = 0; i < s2->length; i++) { if (s1->c_string[i] != s2->c_string[i]) return false; } return true; } //////////////////////////////////////////////////////////////////////////////// // ss_istartswith is the case-insensitive version of ss_startswith. // bool ss_istartswith(SIZED_STRING* s1, SIZED_STRING* s2) { if (s1->length < s2->length) return false; for (uint32_t i = 0; i < s2->length; i++) { if (yr_lowercase[(uint8_t) s1->c_string[i]] != yr_lowercase[(uint8_t) s2->c_string[i]]) return false; } return true; } //////////////////////////////////////////////////////////////////////////////// // ss_endswith returns true if the sized string s1 ends with s2. // bool ss_endswith(SIZED_STRING* s1, SIZED_STRING* s2) { if (s1->length < s2->length) return false; for (uint32_t i = 0; i < s2->length; i++) { if (s1->c_string[s1->length - s2->length + i] != s2->c_string[i]) return false; } return true; } //////////////////////////////////////////////////////////////////////////////// // ss_iendswith is the case-insensitive version of ss_endswith. // bool ss_iendswith(SIZED_STRING* s1, SIZED_STRING* s2) { if (s1->length < s2->length) return false; for (uint32_t i = 0; i < s2->length; i++) { if (yr_lowercase[(uint8_t) s1->c_string[s1->length - s2->length + i]] != yr_lowercase[(uint8_t) s2->c_string[i]]) return false; } return true; } //////////////////////////////////////////////////////////////////////////////// // ss_dup creates a new copy of a given SIZED_STRING. // SIZED_STRING* ss_dup(SIZED_STRING* s) { SIZED_STRING* result = (SIZED_STRING*) yr_malloc( sizeof(SIZED_STRING) + s->length); if (result == NULL) return NULL; result->length = s->length; result->flags = s->flags; memcpy(result->c_string, s->c_string, s->length + 1); return result; } //////////////////////////////////////////////////////////////////////////////// // ss_new creates a SIZED_STRING from a C string. // SIZED_STRING* ss_new(const char* s) { SIZED_STRING* result; size_t length = strlen(s); result = (SIZED_STRING*) yr_malloc(sizeof(SIZED_STRING) + length); if (result == NULL) return NULL; result->length = (uint32_t) length; result->flags = 0; // Copy the string and the null terminator. strcpy(result->c_string, s); return result; } //////////////////////////////////////////////////////////////////////////////// // Convert a SIZED_STRING to a wide version. It is up to the caller to free // the returned string. // SIZED_STRING* ss_convert_to_wide(SIZED_STRING* s) { SIZED_STRING* wide = (SIZED_STRING*) yr_malloc( sizeof(SIZED_STRING) + s->length * 2); if (wide == NULL) return NULL; for (size_t i = 0; i < s->length; i++) { wide->c_string[i * 2] = s->c_string[i]; wide->c_string[i * 2 + 1] = '\x00'; } wide->length = s->length * 2; wide->flags = s->flags | STRING_FLAGS_WIDE; return wide; } yara-4.1.3/libyara/stack.c000066400000000000000000000075711413423160300153560ustar00rootroot00000000000000/* Copyright (c) 2018. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include //////////////////////////////////////////////////////////////////////////////// // Creates a stack for items of the size specified by item_size. All items // in the stack must have the same size. The stack will have an initial // capacity as specified by initial_capacity and will grow as required when // more objects are pushed. // int yr_stack_create(int initial_capacity, int item_size, YR_STACK** stack) { *stack = (YR_STACK*) yr_malloc(sizeof(YR_STACK)); if (*stack == NULL) return ERROR_INSUFFICIENT_MEMORY; (*stack)->items = yr_malloc(initial_capacity * item_size); if ((*stack)->items == NULL) { yr_free(*stack); *stack = NULL; return ERROR_INSUFFICIENT_MEMORY; } (*stack)->capacity = initial_capacity; (*stack)->item_size = item_size; (*stack)->top = 0; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Destroys a stack and deallocates all its resources. // void yr_stack_destroy(YR_STACK* stack) { yr_free(stack->items); yr_free(stack); } //////////////////////////////////////////////////////////////////////////////// // Pushes an item into the stack. If the stack has reached its capacity the // funtion tries to double the capacity. This operation can fail with // ERROR_INSUFFICIENT_MEMORY. // int yr_stack_push(YR_STACK* stack, void* item) { if (stack->top == stack->capacity) { void* items = yr_realloc( stack->items, 2 * stack->capacity * stack->item_size); if (items == NULL) return ERROR_INSUFFICIENT_MEMORY; stack->items = items; stack->capacity *= 2; } memcpy( (uint8_t*) stack->items + stack->top * stack->item_size, item, stack->item_size); stack->top++; return ERROR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // Pops an item from the stack. The caller must pass pointer to a buffer // where the function will copy the item. The buffer must have enough space // to hold the item. Returns 1 if an item could be poped and 0 if the stack // was already empty. // int yr_stack_pop(YR_STACK* stack, void* item) { if (stack->top == 0) // Return 0 if stack is empty. return 0; stack->top--; memcpy( item, (uint8_t*) stack->items + stack->top * stack->item_size, stack->item_size); return 1; } yara-4.1.3/libyara/stino.settings000066400000000000000000000001031413423160300170030ustar00rootroot00000000000000{ "baudrate": 4, "line_ending": 1, "serial_port": 1 }yara-4.1.3/libyara/stopwatch.c000066400000000000000000000102051413423160300162510ustar00rootroot00000000000000/* Copyright (c) 2017. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined(_WIN32) void yr_stopwatch_start(YR_STOPWATCH* sw) { QueryPerformanceFrequency(&sw->frequency); QueryPerformanceCounter(&sw->start); } uint64_t yr_stopwatch_elapsed_ns(YR_STOPWATCH* sw) { LARGE_INTEGER li; QueryPerformanceCounter(&li); return (li.QuadPart - sw->start.QuadPart) * 1000000000ULL / sw->frequency.QuadPart; } #elif defined(__APPLE__) && defined(__MACH__) void yr_stopwatch_start(YR_STOPWATCH* sw) { mach_timebase_info(&sw->timebase); sw->start = mach_absolute_time(); } uint64_t yr_stopwatch_elapsed_ns(YR_STOPWATCH* sw) { uint64_t now = mach_absolute_time(); return ((now - sw->start) * sw->timebase.numer) / sw->timebase.denom; } #elif defined(HAVE_CLOCK_GETTIME) #define timespecsub(tsp, usp, vsp) \ do \ { \ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ if ((vsp)->tv_nsec < 0) \ { \ (vsp)->tv_sec--; \ (vsp)->tv_nsec += 1000000000L; \ } \ } while (0) void yr_stopwatch_start(YR_STOPWATCH* stopwatch) { clock_gettime(CLOCK_MONOTONIC, &stopwatch->ts_start); } uint64_t yr_stopwatch_elapsed_ns(YR_STOPWATCH* stopwatch) { struct timespec ts_stop; struct timespec ts_elapsed; clock_gettime(CLOCK_MONOTONIC, &ts_stop); timespecsub(&ts_stop, &stopwatch->ts_start, &ts_elapsed); return ts_elapsed.tv_sec * 1000000000ULL + ts_elapsed.tv_nsec; } #else #include #define timevalsub(tvp, uvp, vvp) \ do \ { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) \ { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000L; \ } \ } while (0) void yr_stopwatch_start(YR_STOPWATCH* stopwatch) { gettimeofday(&stopwatch->tv_start, NULL); } uint64_t yr_stopwatch_elapsed_ns(YR_STOPWATCH* stopwatch) { struct timeval tv_stop; struct timeval tv_elapsed; gettimeofday(&tv_stop, NULL); timevalsub(&tv_stop, &stopwatch->tv_start, &tv_elapsed); return tv_elapsed.tv_sec * 1000000000ULL + tv_elapsed.tv_usec * 1000ULL; } #endif yara-4.1.3/libyara/stream.c000066400000000000000000000036261413423160300155410ustar00rootroot00000000000000/* Copyright (c) 2015. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include size_t yr_stream_read(void* ptr, size_t size, size_t count, YR_STREAM* stream) { if (stream->read == NULL) return 0; return stream->read(ptr, size, count, stream->user_data); } size_t yr_stream_write( const void* ptr, size_t size, size_t count, YR_STREAM* stream) { if (stream->write == NULL) return 0; return stream->write(ptr, size, count, stream->user_data); } yara-4.1.3/libyara/strutils.c000066400000000000000000000115661413423160300161410ustar00rootroot00000000000000/* Copyright (c) 2007-2014. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include uint64_t xtoi(const char* hexstr) { size_t i; size_t l = strlen(hexstr); uint64_t r = 0; for (i = 0; i < l; i++) { switch (hexstr[i]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': r |= ((uint64_t)(hexstr[i] - '0')) << ((l - i - 1) * 4); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': r |= ((uint64_t)(hexstr[i] - 'a' + 10)) << ((l - i - 1) * 4); break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': r |= ((uint64_t)(hexstr[i] - 'A' + 10)) << ((l - i - 1) * 4); break; default: i = l; // force loop exit } } return r; } /* strlcpy and strlcat are defined in FreeBSD and OpenBSD, the following implementations were taken from OpenBSD. */ #if !HAVE_STRLCPY && !defined(strlcpy) size_t strlcpy(char* dst, const char* src, size_t size) { register char* d = dst; register const char* s = src; register size_t n = size; // Copy as many bytes as will fit if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } // Not enough room in dst, add NUL and traverse rest of src if (n == 0) { if (size != 0) *d = '\0'; // NULL-terminate dst while (*s++) ; } return (s - src - 1); // count does not include NULL } #endif #if !HAVE_STRLCAT && !defined(strlcat) size_t strlcat(char* dst, const char* src, size_t size) { register char* d = dst; register const char* s = src; register size_t n = size; size_t dlen; // Find the end of dst and adjust bytes left but don't go past end while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = size - dlen; if (n == 0) return (dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return (dlen + (s - src)); // count does not include NULL } #endif int strnlen_w(const char* w_str) { int len = 0; while (w_str[0] || w_str[1]) { w_str += 2; len += 1; } return len; } int strcmp_w(const char* w_str, const char* str) { while (*str != 0 && w_str[0] == *str && w_str[1] == 0) { w_str += 2; str += 1; } // Higher-order byte of wide char non-zero? -> w_str is larger than str if (w_str[1] != 0) return 1; return w_str[0] - *str; } size_t strlcpy_w(char* dst, const char* w_src, size_t n) { register char* d = dst; register const char* s = w_src; while (n > 1 && *s != 0) { *d = *s; d += 1; n -= 1; s += 2; } while (*s) s += 2; *d = '\0'; return (s - w_src) / 2; } #if !HAVE_MEMMEM && !defined(memmem) void* memmem( const void* haystack, size_t haystack_size, const void* needle, size_t needle_size) { char* sp = (char*) haystack; char* pp = (char*) needle; char* eos; if (haystack == NULL || haystack_size == 0 || needle == NULL || needle_size == 0) return NULL; eos = sp + haystack_size - needle_size; while (sp <= eos) { if (*sp == *pp && memcmp(sp, pp, needle_size) == 0) return sp; sp++; } return NULL; } #endif int yr_isalnum(const uint8_t* s) { return (*s >= 0x30 && *s <= 0x39) || (*s >= 0x41 && *s <= 0x5a) || (*s >= 0x61 && *s <= 0x7a); } yara-4.1.3/libyara/threading.c000066400000000000000000000103261413423160300162060ustar00rootroot00000000000000/* Copyright (c) 2016. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #if defined(_WIN32) || defined(__CYGWIN__) YR_THREAD_ID yr_current_thread_id(void) { return GetCurrentThreadId(); } int yr_mutex_create(YR_MUTEX* mutex) { *mutex = CreateMutex(NULL, FALSE, NULL); if (*mutex == NULL) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_mutex_destroy(YR_MUTEX* mutex) { if (CloseHandle(*mutex) == FALSE) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_mutex_lock(YR_MUTEX* mutex) { if (WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_mutex_unlock(YR_MUTEX* mutex) { if (ReleaseMutex(*mutex) == FALSE) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_thread_storage_create(YR_THREAD_STORAGE_KEY* storage) { *storage = TlsAlloc(); if (*storage == TLS_OUT_OF_INDEXES) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_thread_storage_destroy(YR_THREAD_STORAGE_KEY* storage) { if (TlsFree(*storage) == FALSE) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_thread_storage_set_value(YR_THREAD_STORAGE_KEY* storage, void* value) { if (TlsSetValue(*storage, value) == FALSE) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } void* yr_thread_storage_get_value(YR_THREAD_STORAGE_KEY* storage) { return TlsGetValue(*storage); } #else // POSIX implementation YR_THREAD_ID yr_current_thread_id(void) { return pthread_self(); } int yr_mutex_create(YR_MUTEX* mutex) { if (pthread_mutex_init(mutex, NULL) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_mutex_destroy(YR_MUTEX* mutex) { if (pthread_mutex_destroy(mutex) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_mutex_lock(YR_MUTEX* mutex) { if (pthread_mutex_lock(mutex) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_mutex_unlock(YR_MUTEX* mutex) { if (pthread_mutex_unlock(mutex) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_thread_storage_create(YR_THREAD_STORAGE_KEY* storage) { if (pthread_key_create(storage, NULL) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_thread_storage_destroy(YR_THREAD_STORAGE_KEY* storage) { if (pthread_key_delete(*storage) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } int yr_thread_storage_set_value(YR_THREAD_STORAGE_KEY* storage, void* value) { if (pthread_setspecific(*storage, value) != 0) return ERROR_INTERNAL_FATAL_ERROR; return ERROR_SUCCESS; } void* yr_thread_storage_get_value(YR_THREAD_STORAGE_KEY* storage) { return pthread_getspecific(*storage); } #endif yara-4.1.3/libyara/yara.pc.in000066400000000000000000000005031413423160300157560ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ includedir=@includedir@ libdir=@libdir@ Name: yara Description: YARA library URL: https://virustotal.github.io/yara/ Version: @PACKAGE_VERSION@ Requires.private: @PC_REQUIRES_PRIVATE@ Cflags: -I${includedir} Libs: -L${libdir} -lyara Libs.private: @PC_LIBS_PRIVATE@ @PTHREAD_LIBS@ yara-4.1.3/m4/000077500000000000000000000000001413423160300127705ustar00rootroot00000000000000yara-4.1.3/m4/acx_pthread.m4000066400000000000000000000324771413423160300155310ustar00rootroot00000000000000# This was retrieved from # http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi # See also (perhaps for new versions?) # http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi # # We've rewritten the inconsistency check code (from avahi), to work # more broadly. In particular, it no longer assumes ld accepts -zdefs. # This caused a restructuring of the code, but the functionality has only # changed a little. dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) dnl dnl @summary figure out how to build C programs using POSIX threads dnl dnl This macro figures out how to build C programs using POSIX threads. dnl It sets the PTHREAD_LIBS output variable to the threads library and dnl linker flags, and the PTHREAD_CFLAGS output variable to any special dnl C compiler flags that are needed. (The user can also force certain dnl compiler flags/libs to be tested by setting these environment dnl variables.) dnl dnl Also sets PTHREAD_CC to any special C compiler that is needed for dnl multi-threaded programs (defaults to the value of CC otherwise). dnl (This is necessary on AIX to use the special cc_r compiler alias.) dnl dnl NOTE: You are assumed to not only compile your program with these dnl flags, but also link it with them as well. e.g. you should link dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS dnl $LIBS dnl dnl If you are only building threads programs, you may wish to use dnl these variables in your default LIBS, CFLAGS, and CC: dnl dnl LIBS="$PTHREAD_LIBS $LIBS" dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" dnl CC="$PTHREAD_CC" dnl dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). dnl dnl ACTION-IF-FOUND is a list of shell commands to run if a threads dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the dnl default action will define HAVE_PTHREAD. dnl dnl Please let the authors know if this macro fails on any platform, or dnl if you have any other suggestions or comments. This macro was based dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. dnl We are also grateful for the helpful feedback of numerous users. dnl dnl @category InstalledPackages dnl @author Steven G. Johnson dnl @version 2006-05-29 dnl @license GPLWithACException dnl dnl Checks for GCC shared/pthread inconsistency based on work by dnl Marcin Owsiany AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [acx_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) if test "x$acx_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include ], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi # The next part tries to detect GCC inconsistency with -shared on some # architectures and systems. The problem is that in certain # configurations, when -shared is specified, GCC "forgets" to # internally use various flags which are still necessary. # # Prepare the flags # save_CFLAGS="$CFLAGS" save_LIBS="$LIBS" save_CC="$CC" # Try with the flags determined by the earlier checks. # # -Wl,-z,defs forces link-time symbol resolution, so that the # linking checks with -shared actually have any value # # FIXME: -fPIC is required for -shared on many architectures, # so we specify it here, but the right way would probably be to # properly detect whether it is actually required. CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CC="$PTHREAD_CC" # In order not to create several levels of indentation, we test # the value of "$done" until we find the cure or run out of ideas. done="no" # First, make sure the CFLAGS we added are actually accepted by our # compiler. If not (and OS X's ld, for instance, does not accept -z), # then we can't do this test. if test x"$done" = xno; then AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) AC_TRY_LINK(,, , [done=yes]) if test "x$done" = xyes ; then AC_MSG_RESULT([no]) else AC_MSG_RESULT([yes]) fi fi if test x"$done" = xno; then AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [done=yes]) if test "x$done" = xyes; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi fi # # Linux gcc on some architectures such as mips/mipsel forgets # about -lpthread # if test x"$done" = xno; then AC_MSG_CHECKING([whether -lpthread fixes that]) LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [done=yes]) if test "x$done" = xyes; then AC_MSG_RESULT([yes]) PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" else AC_MSG_RESULT([no]) fi fi # # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc # if test x"$done" = xno; then AC_MSG_CHECKING([whether -lc_r fixes that]) LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [done=yes]) if test "x$done" = xyes; then AC_MSG_RESULT([yes]) PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" else AC_MSG_RESULT([no]) fi fi if test x"$done" = xno; then # OK, we have run out of ideas AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) # so it's not safe to assume that we may use pthreads acx_pthread_ok=no fi CFLAGS="$save_CFLAGS" LIBS="$save_LIBS" CC="$save_CC" else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl ACX_PTHREAD yara-4.1.3/sample.file000066400000000000000000000000061413423160300145660ustar00rootroot00000000000000abbbb yara-4.1.3/sample.rules000066400000000000000000000002621413423160300150050ustar00rootroot00000000000000import "pe" rule UPX : Packer { strings: $a = {60 E8 00 00 00 00 58 83 E8 3D 50 8D B8} condition: $a at pe.entry_point } rule test {condition: false} yara-4.1.3/sandbox/000077500000000000000000000000001413423160300141065ustar00rootroot00000000000000yara-4.1.3/sandbox/BUILD.bazel000066400000000000000000000105751413423160300157740ustar00rootroot00000000000000# Copyright (c) 2019. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. load("@rules_cc//cc:defs.bzl", "cc_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") load( "@com_google_sandboxed_api//sandboxed_api/bazel:proto.bzl", "sapi_proto_library", ) load( "@com_google_sandboxed_api//sandboxed_api/bazel:sapi.bzl", "sapi_library", ) # Proto message that stores YARA matches. Used to communicate matches from # the sandboxee to the host code. sapi_proto_library( name = "yara_matches", srcs = ["yara_matches.proto"], ) # Library with a callback function to collect YARA matches into a YaraMatches # proto cc_library( name = "collect_matches", srcs = ["collect_matches.cc"], hdrs = ["collect_matches.h"], visibility = ["//visibility:public"], deps = [ ":yara_matches_cc_proto", "//:yara", ], ) # The sandboxee side of the YARA sandbox. This implements a dispatch queue # shared by multiple worker threads. YARA rules are shared across all threads # to keep memory usage down. cc_library( name = "yara_entry_points", srcs = ["yara_entry_points.cc"], deps = [ ":collect_matches", ":yara_matches_cc_proto", "//:libyara", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:node_hash_map", "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", ], alwayslink = 1, ) # Sandboxed API for YARA. This is what clients of this library should use. The # API is intentionally minimal and may be extended in the future. # See the "sandboxed-yara" target for an example on how to use this from code. sapi_library( name = "yara_sapi", srcs = ["yara_transaction.cc"], hdrs = ["yara_transaction.h"], embed = True, functions = [ "YaraAsyncScanFd", "YaraGetScanResult", "YaraInitWorkers", "YaraLoadRules", ], input_files = ["yara_entry_points.cc"], lib = ":yara_entry_points", lib_name = "Yara", namespace = "yara::sandbox", visibility = ["//visibility:public"], deps = [ ":yara_matches_cc_proto", "//:yara_errors", "@com_google_absl//absl/memory", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_sandboxed_api//sandboxed_api/sandbox2/util:bpf_helper", "@com_google_sandboxed_api//sandboxed_api/util:status", ], ) cc_test( name = "yara_transaction_test", srcs = ["yara_transaction_test.cc"], deps = [ ":yara_sapi", "@com_google_googletest//:gtest_main", "@com_google_sandboxed_api//sandboxed_api/util:status_matchers", ], ) # Sandboxed command-line executable demonstrating how to use the YARA SAPI. cc_binary( name = "sandboxed_yara", srcs = ["sandboxed_yara.cc"], deps = [ ":yara_sapi", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/strings", ], ) yara-4.1.3/sandbox/collect_matches.cc000066400000000000000000000051041413423160300175460ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sandbox/collect_matches.h" #include "libyara/include/yara.h" #include "sandbox/yara_matches.pb.h" namespace yara { int CollectMatches( YR_SCAN_CONTEXT*, int message, void* message_data, void* user_data) { if (message != CALLBACK_MSG_RULE_MATCHING) { return ERROR_SUCCESS; // There are no matching rules, simply return } auto* rule = static_cast(message_data); YR_META* rule_meta = rule->metas; auto* match = reinterpret_cast(user_data)->add_match(); if (rule->ns != nullptr && rule->ns->name != nullptr) { match->mutable_id()->set_rule_namespace(rule->ns->name); } match->mutable_id()->set_rule_name(rule->identifier); yr_rule_metas_foreach(rule, rule_meta) { auto* meta = match->add_meta(); meta->set_identifier(rule_meta->identifier); switch (rule_meta->type) { case META_TYPE_BOOLEAN: case META_TYPE_INTEGER: meta->set_int_value(rule_meta->integer); break; case META_TYPE_STRING: meta->set_bytes_value(rule_meta->string); break; } } return ERROR_SUCCESS; } } // namespace yara yara-4.1.3/sandbox/collect_matches.h000066400000000000000000000035411413423160300174130ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SANDBOX_COLLECT_MATCHES_H_ #define SANDBOX_COLLECT_MATCHES_H_ struct YR_SCAN_CONTEXT; namespace yara { // Callback function for yr_scan_mem() that collects YARA matches in a // YaraMatches proto given in user_data. int CollectMatches(YR_SCAN_CONTEXT*, int message, void* message_data, void* user_data); } // namespace yara #endif // SANDBOX_COLLECT_MATCHES_H_ yara-4.1.3/sandbox/sandboxed_yara.cc000066400000000000000000000113651413423160300174060ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "sandbox/yara_transaction.h" #include "sandboxed_api/util/statusor.h" // TODO(cblichmann): SAPI leaks these symbols currently. #undef ABSL_FLAG #undef ABSL_DECLARE_FLAG #undef ABSL_RETIRED_FLAG #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/flags/usage.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/time/time.h" ABSL_FLAG(std::string, identifier, "", "print only rules with this name"); ABSL_FLAG(int, timeout, 5, "abort scanning after the given number of seconds"); namespace yara { namespace { ::sapi::StatusOr ReadFileToString(absl::string_view filename) { std::ifstream input(std::string(filename), std::ios::in | std::ios::binary); std::ostringstream output; output << input.rdbuf(); if (!input) { return absl::UnknownError( absl::StrCat("Cannot read file '", filename, "'")); } return output.str(); } } // namespace // Implements a subset of the YARA command line scanner, but runs the actual // scan inside of a sandbox. absl::Status YaraMain(const std::vector& args) { if (args.size() < 3) { return absl::InvalidArgumentError("Missing operand. Try '--help'."); } // Get file to scan and concatenate all the YARA rules from the specified // files. std::string scan_filename = args.back(); std::string all_rules; for (size_t i = 1; i != args.size() - 1; ++i) { SAPI_ASSIGN_OR_RETURN(std::string rules, ReadFileToString(args[i])); absl::StrAppend(&all_rules, rules, "\n"); } SAPI_ASSIGN_OR_RETURN( auto transaction, YaraTransaction::Create( YaraTransaction::Options() .set_scan_timeout(absl::Seconds(absl::GetFlag(FLAGS_timeout))) .set_num_workers(1))); SAPI_ASSIGN_OR_RETURN( int num_rules ABSL_ATTRIBUTE_UNUSED, transaction->LoadRules(all_rules)); struct FDCloser { ~FDCloser() { close(fd); } int fd; } fd_closer{open(scan_filename.c_str(), O_RDONLY)}; if (fd_closer.fd == -1) { return absl::UnknownError(absl::StrCat( "Cannot open file '", scan_filename, "': ", strerror(errno))); } SAPI_ASSIGN_OR_RETURN(YaraMatches matches, transaction->ScanFd(fd_closer.fd)); for (const auto& match : matches.match()) { const std::string& rule_name = match.id().rule_name(); if (absl::GetFlag(FLAGS_identifier).empty() || (absl::GetFlag(FLAGS_identifier) == rule_name)) { absl::PrintF("%s %s\n", rule_name, scan_filename); } } return absl::OkStatus(); } } // namespace yara int main(int argc, char* argv[]) { absl::string_view argv0 = argv[0]; { auto last_slash_pos = argv0.find_last_of("/\\"); if (last_slash_pos != absl::string_view::npos) { argv0 = argv0.substr(last_slash_pos + 1); } } absl::SetProgramUsageMessage(absl::StrCat( "YARA, the pattern matching swiss army knife.\n", "Usage: ", argv0, " [OPTION] RULES_FILE... FILE")); absl::Status status = ::yara::YaraMain(absl::ParseCommandLine(argc, argv)); if (!status.ok()) { absl::FPrintF(stderr, "ERROR: %s\n", status.message()); return EXIT_FAILURE; } return EXIT_SUCCESS; } yara-4.1.3/sandbox/yara_entry_points.cc000066400000000000000000000167241413423160300202000ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "absl/base/attributes.h" #include "absl/container/node_hash_map.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "libyara/include/yara.h" #include "sandbox/collect_matches.h" #include "sandbox/yara_matches.pb.h" namespace yara { namespace { struct ScanTask { // Key into the g_results map, used by YaraGetScanResult() uint64_t result_id; // File descriptor containing the data to scan int data_fd; // File descriptor used to signal the host code on scan completion int event_fd; // Scan timeout. YARA only supports second granularity. absl::Duration timeout; }; struct ScanResult { int code; YaraMatches matches; }; static const bool g_init_done ABSL_ATTRIBUTE_UNUSED = []() { // Disable output buffering setbuf(stdout, nullptr); setbuf(stderr, nullptr); // Increase stack size struct rlimit stack_limit; stack_limit.rlim_cur = 1 << 20 /* 1 MiB */; stack_limit.rlim_max = stack_limit.rlim_cur; ABSL_RAW_CHECK(setrlimit(RLIMIT_STACK, &stack_limit) == 0, strerror(errno)); // Initialize YARA. Note that the sandboxed code never calls yr_finalize(). // Instead, the OS will clean up on process exit. const int err = yr_initialize(); ABSL_RAW_CHECK( err == ERROR_SUCCESS, absl::StrCat("yr_initialize() failed with code: ", err).c_str()); return true; }(); // Global dispatch queue used to schedule new scan tasks ABSL_CONST_INIT static absl::Mutex g_queue_mutex(absl::kConstInit); static auto* g_queue GUARDED_BY(g_queue_mutex) = new std::queue(); static uint64_t g_result_id GUARDED_BY(g_queue_mutex) = 0; // This map tracks scan results. It relies on pointers staying stable, so this // uses a node_hash_map<> instead of a flat_hash_map<>. ABSL_CONST_INIT static absl::Mutex g_results_mutex(absl::kConstInit); static auto* g_results GUARDED_BY(g_results_mutex) = new absl::node_hash_map(); ABSL_CONST_INIT static absl::Mutex g_rules_mutex(absl::kConstInit); static YR_RULES* g_rules GUARDED_BY(g_rules_mutex) = nullptr; void ScanWorker() { while (true) { // Wait for and retrieve a new ScanTask from the queue. g_queue_mutex.LockWhen(absl::Condition( +[](std::queue* queue) { return !queue->empty(); }, g_queue)); const ScanTask task = std::move(g_queue->front()); g_queue->pop(); g_queue_mutex.Unlock(); ScanResult result; { absl::ReaderMutexLock lock(&g_rules_mutex); result.code = yr_rules_scan_fd( g_rules, task.data_fd, // Disable SIGSEGV handler, allowing YARA to crash/coredump. SCAN_FLAGS_NO_TRYCATCH, CollectMatches, /*user_data=*/reinterpret_cast(&result.matches), absl::ToInt64Seconds(task.timeout)); } { absl::MutexLock lock(&g_results_mutex); (*g_results)[task.result_id] = std::move(result); } // Unblock any waiting clients on the host side. This should always succeed // writing 8 bytes, as long as the event_fd stays open in this function, // hence the CHECK. uint64_t unblock_value = 1; ABSL_RAW_CHECK( write(task.event_fd, &unblock_value, sizeof(unblock_value)) == sizeof(unblock_value), strerror(errno)); close(task.event_fd); close(task.data_fd); } } } // namespace extern "C" void YaraInitWorkers(int num_workers) { const int num_threads = std::min( static_cast(std::min(num_workers, YR_MAX_THREADS)), std::thread::hardware_concurrency()); static auto* workers = new std::vector(); workers->reserve(num_threads); for (int i = 0; i < num_threads; ++i) { workers->emplace_back(ScanWorker); } } // Initializes the global YARA rules set from a string. Returns the number of // rules loaded. Extended error information can be found in status if it is not // nullptr. extern "C" int YaraLoadRules(const char* rule_string, YaraStatus* error_status) { _YR_COMPILER* compiler; int error = yr_compiler_create(&compiler); if (error != ERROR_SUCCESS) { if (error_status) { error_status->set_code(error); } return 0; } std::unique_ptr<_YR_COMPILER, void (*)(_YR_COMPILER*)> compiler_cleanup( compiler, yr_compiler_destroy); if (yr_compiler_add_string(compiler, rule_string, nullptr) != 0) { if (error_status) { error_status->set_code(compiler->last_error); char message[512] = {'\0'}; yr_compiler_get_error_message(compiler, message, sizeof(message)); error_status->set_message(message); } return 0; } YR_RULES* rules = nullptr; error = yr_compiler_get_rules(compiler, &rules); if (error != ERROR_SUCCESS) { if (error_status) { error_status->set_code(error); } return 0; } int num_rules = 0; YR_RULE* rule; yr_rules_foreach(rules, rule) { ++num_rules; } absl::MutexLock lock(&g_rules_mutex); if (g_rules) { yr_rules_destroy(g_rules); } g_rules = rules; return num_rules; } // Schedules a new asynchronous YARA scan task on the data in the specified file // descriptor. Notifies host code via writing to the event_fd file descriptor. // Returns a unique identifier that can be used to retrieve the results. extern "C" uint64_t YaraAsyncScanFd(int data_fd, int event_fd, int timeout_secs) { absl::MutexLock queue_lock(&g_queue_mutex); ++g_result_id; g_queue->push({g_result_id, data_fd, event_fd, absl::Seconds(timeout_secs)}); return g_result_id; } extern "C" int YaraGetScanResult(uint64_t result_id, YaraMatches* matches) { absl::MutexLock lock(&g_results_mutex); auto result = g_results->find(result_id); if (result == g_results->end()) { return -1; } int code = result->second.code; *matches = std::move(result->second.matches); g_results->erase(result); return code; } } // namespace yara yara-4.1.3/sandbox/yara_matches.proto000066400000000000000000000041641413423160300176400ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ syntax = "proto3"; package yara; // Identifies a single rule inside a namespace message YaraRuleId { string rule_namespace = 1; // Currently unused by yara_entrypoints.cc string rule_name = 2; } // Holds N mappings for the matches. message YaraMatches { // Holds one mapping from (namespace, name) --> N key-value entries. message Match { message Meta { string identifier = 1; oneof value { bytes bytes_value = 2; int64 int_value = 3; } } YaraRuleId id = 1; repeated Meta meta = 2; } repeated Match match = 1; } message YaraStatus { int64 code = 1; int64 line_number = 2; string message = 3; } yara-4.1.3/sandbox/yara_transaction.cc000066400000000000000000000132541413423160300177630ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sandbox/yara_transaction.h" #include #include #include #include "absl/strings/str_cat.h" #include "libyara/include/yara/error.h" #include "sandboxed_api/util/status_macros.h" namespace yara { absl::Mutex YaraTransaction::mutex_(absl::kConstInit); ::sapi::StatusOr> YaraTransaction::Create( Options options) { auto transaction = absl::WrapUnique( new YaraTransaction(options.scan_timeout)); // "Run" the transaction in order to initialize the underlying sandbox. SAPI_RETURN_IF_ERROR(transaction->Run()); sandbox::YaraApi api(transaction->sandbox()); SAPI_RETURN_IF_ERROR( api.YaraInitWorkers(options.num_workers >= 1 ? options.num_workers : 1)); return transaction; } ::sapi::StatusOr YaraTransaction::LoadRules(const std::string& rule_string) { absl::MutexLock lock(&mutex_); sandbox::YaraApi api(sandbox()); ::sapi::v::ConstCStr rule_string_sapi(rule_string.c_str()); YaraStatus error_status; ::sapi::v::Proto error_status_sapi(error_status); SAPI_ASSIGN_OR_RETURN( int num_rules, api.YaraLoadRules( rule_string_sapi.PtrBefore(), error_status_sapi.PtrBoth())); if (num_rules <= 0) { auto error_status_copy = error_status_sapi.GetProtoCopy(); if (!error_status_copy) { return absl::UnknownError("Deserialization of response failed"); } return absl::InvalidArgumentError(error_status_copy->message()); } return num_rules; } ::sapi::StatusOr YaraTransaction::ScanFd(int fd) { int local_event_fd = eventfd(0 /* initval */, 0 /* flags */); if (local_event_fd == -1) { return absl::InternalError( absl::StrCat("eventfd() error: ", strerror(errno))); } struct FDCloser { ~FDCloser() { close(event_fd); } int event_fd; } event_fd_closer = {local_event_fd}; sandbox::YaraApi api(sandbox()); uint64_t result_id; { absl::MutexLock lock(&mutex_); // Note: These SAPI Fd objects use the underlying sandbox comms to // synchronize. Hence they must live within this locked scope. ::sapi::v::Fd event_fd(local_event_fd); SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&event_fd)); event_fd.OwnLocalFd(false); // Needs to be valid during poll() event_fd.OwnRemoteFd(false); // Sandboxee will close ::sapi::v::Fd data_fd(fd); SAPI_RETURN_IF_ERROR(sandbox()->TransferToSandboxee(&data_fd)); data_fd.OwnLocalFd(false); // To be closed by caller data_fd.OwnRemoteFd(false); // Sandboxee will close SAPI_ASSIGN_OR_RETURN( result_id, api.YaraAsyncScanFd( data_fd.GetRemoteFd(), event_fd.GetRemoteFd(), absl::ToInt64Seconds(scan_timeout_))); } pollfd poll_events{local_event_fd, POLLIN}; int poll_result; // TEMP_FAILURE_RETRY is a GNU extension that retries if the call returns // EINTR. poll_result = TEMP_FAILURE_RETRY(poll( &poll_events, 1 /* nfds */, // Add extra time to allow code inside the sandbox to time out first. absl::ToInt64Milliseconds(scan_timeout_ + absl::Seconds(10)))); if (poll_result == 0) { return absl::DeadlineExceededError("Scan timeout during poll()"); } if (poll_result == -1) { return absl::InternalError(absl::StrCat("poll() error: ", strerror(errno))); } if (poll_events.revents & POLLHUP || poll_events.revents & POLLERR || poll_events.revents & POLLNVAL) { return absl::InternalError( absl::StrCat("poll() error, revents: ", poll_events.revents)); } absl::MutexLock lock(&mutex_); YaraMatches matches; ::sapi::v::Proto matches_sapi(matches); SAPI_ASSIGN_OR_RETURN( int scan_result, api.YaraGetScanResult(result_id, matches_sapi.PtrBoth())); switch (scan_result) { case ERROR_SUCCESS: case ERROR_TOO_MANY_MATCHES: { auto matches_copy = matches_sapi.GetProtoCopy(); if (!matches_copy) { return absl::UnknownError("Deserialization of response failed"); } return *matches_copy; } case ERROR_SCAN_TIMEOUT: return absl::DeadlineExceededError("Scan timeout"); } return absl::InternalError(absl::StrCat("Error during scan: ", scan_result)); } } // namespace yara yara-4.1.3/sandbox/yara_transaction.h000066400000000000000000000107051413423160300176230ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SANDBOX_TRANSACTION_H_ #define SANDBOX_TRANSACTION_H_ #include #include #include #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "sandbox/yara_matches.pb.h" #include "sandbox/yara_sapi.sapi.h" #include "sandboxed_api/sandbox.h" #include "sandboxed_api/sandbox2/executor.h" #include "sandboxed_api/sandbox2/policy.h" #include "sandboxed_api/sandbox2/util.h" #include "sandboxed_api/sandbox2/util/bpf_helper.h" #include "sandboxed_api/transaction.h" namespace yara { class YaraSandbox : public sandbox::YaraSandbox { public: std::unique_ptr ModifyPolicy( sandbox2::PolicyBuilder* builder) override { return (*builder) .AllowStaticStartup() .AllowMmap() .AllowFork() // Thread creation .AllowSyscalls({ __NR_madvise, __NR_mprotect, __NR_munlock, __NR_poll, __NR_sched_getparam, __NR_sched_getscheduler, __NR_sched_yield, }) .BuildOrDie(); } void ModifyExecutor(sandbox2::Executor* executor) override { (*executor->limits()) // Remove limit on file descriptor bytes. .set_rlimit_fsize(RLIM64_INFINITY) // Wall-time limit per call will be enforced by the Transaction. .set_rlimit_cpu(RLIM64_INFINITY); } }; // Transaction class to run sandboxed Yara scans of the contents of file // descriptors. This class is thread-safe and access to the sandboxee is // multiplexed so that multiple threads can share the transaction. class YaraTransaction : public ::sapi::Transaction { public: struct Options { absl::Duration scan_timeout; int num_workers; Options& set_scan_timeout(absl::Duration value) { scan_timeout = value; return *this; } Options& set_num_workers(int value) { num_workers = value; return *this; } }; // Creates and initializes an instance of this transaction class with the // specified scan_timeout. static ::sapi::StatusOr> Create( Options options = {}); // Loads new Yara rules into the sandboxee. Returns the number of rules // loaded. Only one set of rules can be active at any given time. This method // blocks until all concurrent YARA scans are completed before updating the // rules. ::sapi::StatusOr LoadRules(const std::string& rule_string) LOCKS_EXCLUDED(mutex_); // Scans the contents of the specified file descriptor. // Returns DeadlineExceededError if the scan timed out. ::sapi::StatusOr ScanFd(int fd) LOCKS_EXCLUDED(mutex_); private: explicit YaraTransaction(absl::Duration scan_timeout) : ::sapi::Transaction(absl::make_unique()) {} // Mutex to guard communication with the sandboxee static absl::Mutex mutex_; absl::Duration scan_timeout_; }; } // namespace yara #endif // SANDBOX_TRANSACTION_H_ yara-4.1.3/sandbox/yara_transaction_test.cc000066400000000000000000000130521413423160300210160ustar00rootroot00000000000000/* Copyright (c) 2019. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "sandbox/yara_transaction.h" #include // __NR_memdfd_create #include #include #include #include #include #include "absl/strings/str_cat.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "sandbox/yara_matches.pb.h" #include "sandboxed_api/util/status_matchers.h" #include "sandboxed_api/util/statusor.h" using ::sapi::IsOk; using ::testing::Eq; using ::testing::StrEq; namespace yara { namespace { // Wraps an in-memory file descriptor created by memfd_create(). class MemoryFD { public: static ::sapi::StatusOr CreateWithContent(absl::string_view content) { MemoryFD mem_fd; // Avoid dependency on UAPI headers constexpr uintptr_t MFD_CLOEXEC = 0x0001U; constexpr const char* kName = "memfd"; mem_fd.fd_ = syscall( __NR_memfd_create, reinterpret_cast(kName), MFD_CLOEXEC); if (mem_fd.fd_ == -1) { return absl::UnknownError(absl::StrCat("memfd(): ", strerror(errno))); } if (ftruncate(mem_fd.fd_, content.size()) == -1) { return absl::UnknownError(absl::StrCat("ftruncate(): ", strerror(errno))); } while (!content.empty()) { ssize_t written = TEMP_FAILURE_RETRY( write(mem_fd.fd_, content.data(), content.size())); if (written <= 0) { return absl::UnknownError(absl::StrCat("write(): ", strerror(errno))); } content.remove_prefix(written); } return mem_fd; } MemoryFD(MemoryFD&& other) { *this = std::move(other); } MemoryFD& operator=(MemoryFD&& other) { fd_ = other.fd_; other.fd_ = 0; return *this; } ~MemoryFD() { if (fd_ > 0) { close(fd_); }; } int fd() const { return fd_; } private: MemoryFD() = default; int fd_; }; class TransactionTest : public ::testing::Test { protected: void SetUp() override { SAPI_ASSERT_OK_AND_ASSIGN( transaction_, YaraTransaction::Create(YaraTransaction::Options{} .set_scan_timeout(absl::Minutes(1)) .set_num_workers(16))); } ::sapi::StatusOr ScanString(absl::string_view content) { SAPI_ASSIGN_OR_RETURN( MemoryFD mem_fd, MemoryFD::CreateWithContent(content)); return transaction_->ScanFd(mem_fd.fd()); } std::unique_ptr transaction_; }; TEST_F(TransactionTest, BasicFunctionality) { ASSERT_THAT( transaction_ ->LoadRules(R"( rule Number { strings: $ = "123" condition: all of them } rule Color { strings: $ = "green" condition: all of them } rule Keyboard { strings: $ = "dvorak" condition: all of them })") .ValueOrDie(), Eq(3)); SAPI_ASSERT_OK_AND_ASSIGN(YaraMatches matches, ScanString("qwerty 123")); EXPECT_THAT(matches.match_size(), Eq(1)); EXPECT_THAT(matches.match(0).id().rule_name(), StrEq("Number")); SAPI_ASSERT_OK_AND_ASSIGN(matches, ScanString("green dvorak 456")); EXPECT_THAT(matches.match_size(), Eq(2)); EXPECT_THAT(matches.match(0).id().rule_name(), StrEq("Color")); EXPECT_THAT(matches.match(1).id().rule_name(), StrEq("Keyboard")); } TEST_F(TransactionTest, ConcurrentScanStressTest) { ASSERT_THAT( transaction_ ->LoadRules(R"( rule Simple { strings: $ = "A" condition: all of them })") .ValueOrDie(), Eq(1)); // Large number of threads during testing to increase likelihood of exposing // race conditions in threading code. constexpr int kThreads = 64; std::vector bundle; for (int i = 0; i < kThreads; ++i) { bundle.emplace_back([this, i]() { std::string buf((i + 1) * 102400, 'B'); buf.append("A"); // Force the match to be at the very end SAPI_ASSERT_OK_AND_ASSIGN(YaraMatches matches, ScanString(buf)); ASSERT_THAT(matches.match_size(), Eq(1)); EXPECT_THAT(matches.match(0).id().rule_name(), StrEq("Simple")); }); } for (auto& thread : bundle) { thread.join(); } } } // namespace } // namespace yara yara-4.1.3/tests/000077500000000000000000000000001413423160300136125ustar00rootroot00000000000000yara-4.1.3/tests/BUILD.bazel000066400000000000000000000120421413423160300154670ustar00rootroot00000000000000# Copyright (c) 2019. The YARA Authors. All Rights Reserved. # # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation and/or # other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. COPTS = [ "-D_GNU_SOURCE", "-DHAVE_STDBOOL_H=1", # Tell the compiler we want the C99 standard. "-std=c99", ] cc_library( name = "blob", hdrs = ["blob.h"], ) cc_library( name = "util", srcs = ["util.c"], copts = COPTS, textual_hdrs = ["util.h"], deps = ["@//:libyara"], ) cc_test( name = "test_alignment", srcs = ["test-alignment.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_arena", srcs = ["test-arena.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_atoms", srcs = ["test-atoms.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_rules", srcs = ["test-rules.c"], copts = COPTS, data = [ "data/base64", "data/baz.yar", "data/foo.yar", "data/include/bar.yar", "data/xor.out", "data/xorwide.out", "data/xorwideandascii.out", ], linkstatic = True, deps = [ ":blob", ":util", "@//:libyara", ], ) cc_test( name = "test_pe", srcs = ["test-pe.c"], copts = COPTS, data = [ "data/079a472d22290a94ebb212aa8015cdc8dd28a968c6b4d3b88acdd58ce2d3b885", "data/079a472d22290a94ebb212aa8015cdc8dd28a968c6b4d3b88acdd58ce2d3b885.upx", "data/3b8b90159fa9b6048cc5410c5d53f116943564e4d05b04a843f9b3d0540d0c1c", "data/mtxex.dll", "data/mtxex_modified_rsrc_rva.dll", "data/tiny", "data/tiny-idata-51ff", "data/tiny-idata-5200", "data/tiny-overlay", "data/weird_rich", ], linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_elf", srcs = ["test-elf.c"], copts = COPTS, linkstatic = True, deps = [ ":blob", ":util", "@//:libyara", ], ) cc_test( name = "test_api", srcs = ["test-api.c"], copts = COPTS, data = [ "data/baz.yar", "data/x.txt", ], linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_bitmask", srcs = ["test-bitmask.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_math", srcs = ["test-math.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_stack", srcs = ["test-stack.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) # This test fails when running under ASAN cc_test( name = "test_exception", srcs = ["test-exception.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) cc_test( name = "test_macho", srcs = ["test-macho.c"], copts = COPTS, data = [ "data/tiny-macho", "data/tiny-universal", ], linkstatic = True, deps = [ ":blob", ":util", "@//:libyara", ], ) cc_test( name = "test_dex", srcs = ["test-dex.c"], copts = COPTS, linkstatic = True, deps = [ ":blob", ":util", "@//:libyara", ], ) cc_test( name = "test_re_split", srcs = ["test-re-split.c"], copts = COPTS, linkstatic = True, deps = [ ":util", "@//:libyara", ], ) yara-4.1.3/tests/blob.h000066400000000000000000005200741413423160300147110ustar00rootroot00000000000000/* Copyright (c) 2016. The YARA Authors. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ uint8_t PE32_FILE[] = { 0x4d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x4c, 0x01, 0x01, 0x00, 0x5d, 0xbe, 0x45, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x03, 0x01, 0x0b, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x6a, 0x2a, 0x58, 0xc3, }; uint8_t ELF32_FILE[] = { 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x28, 0x00, 0x04, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x2a, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x00, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x69, 0x64, 0x65, 0x20, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x20, 0x32, 0x2e, 0x30, 0x35, 0x2e, 0x30, 0x31, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x60, 0x80, 0x04, 0x08, 0x60, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; uint8_t ELF64_FILE[] = { 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x40, 0x00, 0x04, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x2a, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x00, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x69, 0x64, 0x65, 0x20, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x65, 0x72, 0x20, 0x32, 0x2e, 0x30, 0x35, 0x2e, 0x30, 0x31, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; uint8_t ELF32_NOSECTIONS[] = { 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x74, 0x80, 0x04, 0x08, 0x74, 0x80, 0x04, 0x08, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x72, 0x5c, 0x33, 0xa6, 0xcd, 0xed, 0x46, 0xf2, 0xc7, 0xa2, 0x8c, 0x1f, 0xbd, 0x65, 0x7a, 0xd1, 0x9f, 0x0f, 0x51, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x2a, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t ELF32_SHAREDOBJ[] = { 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x54, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x05, 0x00, 0x28, 0x00, 0x09, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa8, 0x0f, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa8, 0x0f, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x52, 0xe5, 0x74, 0x64, 0xa8, 0x0f, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x6e, 0x96, 0x9b, 0xbc, 0x8b, 0x0c, 0x9d, 0x95, 0x29, 0xfc, 0x07, 0x04, 0x15, 0x95, 0xc5, 0xf0, 0xb9, 0xd5, 0xcd, 0xae, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x44, 0x06, 0x29, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x45, 0xd5, 0xec, 0xbb, 0xe3, 0x92, 0x7c, 0x32, 0x62, 0xdb, 0xed, 0xd9, 0x71, 0x58, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x07, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x07, 0x00, 0x00, 0x5f, 0x65, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x2a, 0x00, 0x00, 0x00, 0xcd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0xfe, 0xff, 0x6f, 0xf8, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x6e, 0x6f, 0x74, 0x65, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2d, 0x69, 0x64, 0x00, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x65, 0x68, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xf6, 0xff, 0xff, 0x6f, 0x02, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa8, 0x1f, 0x00, 0x00, 0xa8, 0x0f, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; unsigned char ELF32_MIPS_FILE[] = { 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x06, 0x10, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x1f, 0x28, 0x00, 0x00, 0x10, 0x07, 0x00, 0x34, 0x00, 0x20, 0x00, 0x0a, 0x00, 0x28, 0x00, 0x23, 0x00, 0x20, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x40, 0x00, 0x34, 0x00, 0x40, 0x00, 0x34, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x74, 0x00, 0x40, 0x01, 0x74, 0x00, 0x40, 0x01, 0x74, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x90, 0x00, 0x40, 0x01, 0x90, 0x00, 0x40, 0x01, 0x90, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa8, 0x00, 0x40, 0x01, 0xa8, 0x00, 0x40, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xec, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x40, 0x01, 0xc0, 0x00, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x64, 0x74, 0xe5, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x0f, 0xec, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x6d, 0x69, 0x70, 0x73, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x40, 0x09, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x02, 0x80, 0x00, 0x00, 0x00, 0x05, 0x00, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x03, 0x24, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x10, 0x70, 0x00, 0x00, 0x16, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x10, 0x10, 0x70, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x70, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x70, 0x00, 0x00, 0x06, 0x00, 0x40, 0x00, 0x00, 0x70, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x08, 0x70, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x16, 0x70, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x20, 0x70, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x00, 0x07, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x40, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xba, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x40, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x40, 0x06, 0x50, 0x00, 0x00, 0x00, 0x38, 0x12, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x41, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x08, 0x12, 0x02, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x40, 0x09, 0x10, 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x41, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x41, 0x10, 0x70, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x41, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x40, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x00, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x5f, 0x5f, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x4a, 0x76, 0x5f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x00, 0x6c, 0x69, 0x62, 0x63, 0x2e, 0x73, 0x6f, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x49, 0x4e, 0x47, 0x00, 0x5f, 0x5f, 0x52, 0x4c, 0x44, 0x5f, 0x4d, 0x41, 0x50, 0x00, 0x5f, 0x66, 0x74, 0x65, 0x78, 0x74, 0x00, 0x5f, 0x66, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x65, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x66, 0x62, 0x73, 0x73, 0x00, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x5f, 0x5f, 0x67, 0x6e, 0x75, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x67, 0x70, 0x00, 0x5f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x00, 0x00, 0x00, 0x27, 0xbd, 0xff, 0xe0, 0xaf, 0xbc, 0x00, 0x18, 0xaf, 0xbf, 0x00, 0x1c, 0x04, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x99, 0x80, 0x24, 0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x08, 0x0c, 0x04, 0x11, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbc, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x99, 0x80, 0x24, 0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x08, 0x90, 0x04, 0x11, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbc, 0x00, 0x18, 0x8f, 0xbf, 0x00, 0x1c, 0x03, 0xe0, 0x00, 0x08, 0x27, 0xbd, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x08, 0x24, 0x02, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x04, 0x00, 0x00, 0xf0, 0x21, 0xff, 0xfe, 0x76, 0x18, 0xff, 0xfe, 0x76, 0x50, 0xff, 0xfe, 0x71, 0xc0, 0x8f, 0xfc, 0x00, 0x00, 0x03, 0xfc, 0xe0, 0x23, 0x03, 0xa0, 0x20, 0x21, 0x8f, 0xe5, 0x00, 0x08, 0x00, 0xbc, 0x28, 0x21, 0x8f, 0xf9, 0x00, 0x04, 0x03, 0x3c, 0xc8, 0x21, 0x24, 0x01, 0xff, 0xf8, 0x03, 0xa1, 0xe8, 0x24, 0x03, 0x20, 0xf8, 0x09, 0x27, 0xbd, 0xff, 0xf0, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x82, 0x80, 0x18, 0x27, 0xbd, 0xff, 0xd8, 0x8c, 0x85, 0x00, 0x00, 0x24, 0x86, 0x00, 0x04, 0x8f, 0x87, 0x80, 0x1c, 0x8f, 0x84, 0x80, 0x20, 0x8f, 0x99, 0x80, 0x38, 0xaf, 0xbf, 0x00, 0x24, 0xaf, 0xbc, 0x00, 0x18, 0xaf, 0xa0, 0x00, 0x14, 0x03, 0x20, 0xf8, 0x09, 0xaf, 0xa2, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x04, 0x00, 0x41, 0x3c, 0x02, 0x00, 0x41, 0x24, 0x84, 0x10, 0x04, 0x24, 0x42, 0x10, 0x07, 0x00, 0x44, 0x10, 0x23, 0x2c, 0x42, 0x00, 0x07, 0x14, 0x40, 0x00, 0x08, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x99, 0x80, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x04, 0x00, 0x41, 0x3c, 0x02, 0x00, 0x41, 0x24, 0x84, 0x10, 0x04, 0x24, 0x42, 0x10, 0x04, 0x00, 0x44, 0x10, 0x23, 0x00, 0x02, 0x10, 0x83, 0x00, 0x02, 0x2f, 0xc2, 0x00, 0xa2, 0x28, 0x21, 0x00, 0x05, 0x28, 0x43, 0x10, 0xa0, 0x00, 0x08, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x99, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x27, 0xbd, 0xff, 0xd0, 0xaf, 0xb3, 0x00, 0x28, 0x3c, 0x13, 0x00, 0x41, 0x3c, 0x1c, 0x00, 0x42, 0x92, 0x62, 0x10, 0x50, 0x27, 0x9c, 0x90, 0x00, 0xaf, 0xbf, 0x00, 0x2c, 0xaf, 0xb2, 0x00, 0x24, 0xaf, 0xb1, 0x00, 0x20, 0xaf, 0xb0, 0x00, 0x1c, 0x14, 0x40, 0x00, 0x28, 0xaf, 0xbc, 0x00, 0x10, 0x3c, 0x11, 0x00, 0x41, 0x3c, 0x02, 0x00, 0x41, 0x24, 0x42, 0x0f, 0xf8, 0x26, 0x31, 0x0f, 0xf4, 0x00, 0x51, 0x88, 0x23, 0x3c, 0x10, 0x00, 0x41, 0x8e, 0x02, 0x10, 0x54, 0x00, 0x11, 0x88, 0x83, 0x26, 0x31, 0xff, 0xff, 0x3c, 0x04, 0x00, 0x41, 0x00, 0x51, 0x18, 0x2b, 0x24, 0x92, 0x0f, 0xf4, 0x10, 0x60, 0x00, 0x0c, 0x24, 0x42, 0x00, 0x01, 0x00, 0x02, 0x18, 0x80, 0x02, 0x43, 0x18, 0x21, 0x8c, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0xf8, 0x09, 0xae, 0x02, 0x10, 0x54, 0x8e, 0x02, 0x10, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x18, 0x2b, 0x14, 0x60, 0xff, 0xf6, 0x24, 0x42, 0x00, 0x01, 0x0c, 0x10, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbc, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x82, 0x80, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x06, 0x24, 0x02, 0x00, 0x01, 0x8f, 0x99, 0x80, 0x44, 0x3c, 0x04, 0x00, 0x40, 0x03, 0x20, 0xf8, 0x09, 0x24, 0x84, 0x09, 0x58, 0x24, 0x02, 0x00, 0x01, 0xa2, 0x62, 0x10, 0x50, 0x8f, 0xbf, 0x00, 0x2c, 0x8f, 0xb3, 0x00, 0x28, 0x8f, 0xb2, 0x00, 0x24, 0x8f, 0xb1, 0x00, 0x20, 0x8f, 0xb0, 0x00, 0x1c, 0x03, 0xe0, 0x00, 0x08, 0x27, 0xbd, 0x00, 0x30, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x82, 0x80, 0x30, 0x27, 0xbd, 0xff, 0xe0, 0xaf, 0xbf, 0x00, 0x1c, 0x10, 0x40, 0x00, 0x08, 0xaf, 0xbc, 0x00, 0x10, 0x8f, 0x99, 0x80, 0x30, 0x3c, 0x05, 0x00, 0x41, 0x3c, 0x04, 0x00, 0x40, 0x24, 0xa5, 0x10, 0x58, 0x03, 0x20, 0xf8, 0x09, 0x24, 0x84, 0x09, 0x58, 0x8f, 0xbc, 0x00, 0x10, 0x3c, 0x02, 0x00, 0x41, 0x24, 0x44, 0x0f, 0xfc, 0x8c, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbf, 0x00, 0x1c, 0x08, 0x10, 0x01, 0xb5, 0x27, 0xbd, 0x00, 0x20, 0x8f, 0x99, 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0xff, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0xf8, 0x09, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbc, 0x00, 0x10, 0x10, 0x00, 0xff, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x41, 0x8c, 0x59, 0x0f, 0xec, 0x24, 0x02, 0xff, 0xff, 0x13, 0x22, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x27, 0xbd, 0xff, 0xd8, 0xaf, 0xb0, 0x00, 0x1c, 0x3c, 0x10, 0x00, 0x41, 0xaf, 0xb1, 0x00, 0x20, 0xaf, 0xbf, 0x00, 0x24, 0x26, 0x10, 0x0f, 0xec, 0x24, 0x11, 0xff, 0xff, 0x03, 0x20, 0xf8, 0x09, 0x26, 0x10, 0xff, 0xfc, 0x8e, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x31, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbf, 0x00, 0x24, 0x8f, 0xb1, 0x00, 0x20, 0x8f, 0xb0, 0x00, 0x1c, 0x27, 0xbd, 0x00, 0x28, 0x03, 0xe0, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x99, 0x80, 0x10, 0x03, 0xe0, 0x78, 0x21, 0x03, 0x20, 0xf8, 0x09, 0x24, 0x18, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xbd, 0xff, 0xe0, 0xaf, 0xbc, 0x00, 0x18, 0xaf, 0xbf, 0x00, 0x1c, 0x04, 0x11, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1c, 0x00, 0x42, 0x27, 0x9c, 0x90, 0x00, 0x8f, 0x99, 0x80, 0x24, 0x00, 0x00, 0x00, 0x00, 0x27, 0x39, 0x07, 0x24, 0x04, 0x11, 0xff, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbc, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x8f, 0xbc, 0x00, 0x18, 0x8f, 0xbf, 0x00, 0x1c, 0x03, 0xe0, 0x00, 0x08, 0x27, 0xbd, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x09, 0x10, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x40, 0x06, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x35, 0x2e, 0x33, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x50, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x50, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x40, 0x09, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, 0x00, 0x01, 0x13, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0xe8, 0x00, 0x00, 0x00, 0x10, 0x00, 0x40, 0x09, 0x48, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x0c, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0d, 0x00, 0x40, 0x06, 0x50, 0x00, 0x00, 0x00, 0x38, 0x01, 0x9c, 0x00, 0x00, 0x00, 0x7b, 0x03, 0x70, 0x00, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x2e, 0x04, 0x00, 0x00, 0x01, 0x49, 0x01, 0x10, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x4d, 0x05, 0x00, 0x40, 0x06, 0x88, 0x06, 0x01, 0x56, 0x05, 0xf3, 0x01, 0x54, 0x23, 0x04, 0x06, 0x02, 0x8d, 0x14, 0x01, 0x30, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, 0x00, 0x81, 0x08, 0x04, 0x05, 0x00, 0x00, 0x00, 0xf4, 0x09, 0x04, 0x05, 0x69, 0x6e, 0x74, 0x00, 0x07, 0x04, 0x00, 0x00, 0x00, 0x95, 0x07, 0x04, 0x00, 0x00, 0x00, 0x9b, 0x08, 0x01, 0x06, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x83, 0x04, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x10, 0x63, 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x70, 0x73, 0x2f, 0x63, 0x72, 0x74, 0x69, 0x2e, 0x73, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x61, 0x6c, 0x62, 0x69, 0x6e, 0x6f, 0x6c, 0x6f, 0x62, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x2f, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x2d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x36, 0x00, 0x47, 0x4e, 0x55, 0x20, 0x41, 0x53, 0x20, 0x32, 0x2e, 0x32, 0x35, 0x2e, 0x31, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x95, 0x04, 0x01, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x30, 0x63, 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x70, 0x73, 0x2f, 0x63, 0x72, 0x74, 0x6e, 0x2e, 0x73, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x61, 0x6c, 0x62, 0x69, 0x6e, 0x6f, 0x6c, 0x6f, 0x62, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x2f, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x2d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x36, 0x00, 0x47, 0x4e, 0x55, 0x20, 0x41, 0x53, 0x20, 0x32, 0x2e, 0x32, 0x35, 0x2e, 0x31, 0x00, 0x80, 0x01, 0x01, 0x11, 0x01, 0x25, 0x0e, 0x13, 0x0b, 0x03, 0x0e, 0x1b, 0x0e, 0x55, 0x17, 0x11, 0x01, 0x10, 0x17, 0x00, 0x00, 0x02, 0x2e, 0x01, 0x3f, 0x19, 0x03, 0x0e, 0x3a, 0x0b, 0x3b, 0x0b, 0x27, 0x19, 0x87, 0x01, 0x19, 0x11, 0x01, 0x12, 0x06, 0x40, 0x18, 0x97, 0x42, 0x19, 0x01, 0x13, 0x00, 0x00, 0x03, 0x05, 0x00, 0x03, 0x08, 0x3a, 0x0b, 0x3b, 0x0b, 0x49, 0x13, 0x02, 0x17, 0x00, 0x00, 0x04, 0x34, 0x00, 0x03, 0x0e, 0x3a, 0x0b, 0x3b, 0x0b, 0x49, 0x13, 0x02, 0x17, 0x00, 0x00, 0x05, 0x89, 0x82, 0x01, 0x01, 0x11, 0x01, 0x00, 0x00, 0x06, 0x8a, 0x82, 0x01, 0x00, 0x02, 0x18, 0x91, 0x42, 0x18, 0x00, 0x00, 0x07, 0x0f, 0x00, 0x0b, 0x0b, 0x49, 0x13, 0x00, 0x00, 0x08, 0x24, 0x00, 0x0b, 0x0b, 0x3e, 0x0b, 0x03, 0x0e, 0x00, 0x00, 0x09, 0x24, 0x00, 0x0b, 0x0b, 0x3e, 0x0b, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x10, 0x06, 0x55, 0x06, 0x03, 0x08, 0x1b, 0x08, 0x25, 0x08, 0x13, 0x05, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x10, 0x06, 0x55, 0x06, 0x03, 0x08, 0x1b, 0x08, 0x25, 0x08, 0x13, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x01, 0x01, 0xfb, 0x0e, 0x0d, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x63, 0x72, 0x74, 0x00, 0x00, 0x63, 0x72, 0x74, 0x31, 0x2e, 0x63, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x40, 0x06, 0x50, 0x03, 0x0d, 0x01, 0x85, 0x47, 0x4d, 0x08, 0x39, 0x4a, 0x4d, 0x02, 0x0c, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x01, 0x01, 0xfb, 0x0e, 0x0d, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x63, 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x70, 0x73, 0x00, 0x00, 0x63, 0x72, 0x74, 0x69, 0x2e, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x40, 0x05, 0x8c, 0x19, 0x4b, 0x4b, 0x02, 0x04, 0x00, 0x01, 0x01, 0x00, 0x05, 0x02, 0x00, 0x40, 0x09, 0x10, 0x03, 0x10, 0x01, 0x4b, 0x4b, 0x02, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x01, 0x01, 0xfb, 0x0e, 0x0d, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x63, 0x72, 0x74, 0x2f, 0x6d, 0x69, 0x70, 0x73, 0x00, 0x00, 0x63, 0x72, 0x74, 0x6e, 0x2e, 0x73, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x40, 0x05, 0xe8, 0x15, 0x4b, 0x4b, 0x4b, 0x02, 0x04, 0x00, 0x01, 0x01, 0x00, 0x05, 0x02, 0x00, 0x40, 0x09, 0x48, 0x03, 0x09, 0x01, 0x4b, 0x4b, 0x4b, 0x02, 0x04, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x01, 0x7c, 0x1f, 0x0d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x50, 0x00, 0x00, 0x00, 0x38, 0x50, 0x0e, 0x28, 0x58, 0x9f, 0x01, 0x00, 0x00, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x00, 0x61, 0x72, 0x67, 0x63, 0x00, 0x47, 0x4e, 0x55, 0x20, 0x43, 0x39, 0x39, 0x20, 0x35, 0x2e, 0x33, 0x2e, 0x30, 0x20, 0x2d, 0x6d, 0x65, 0x62, 0x20, 0x2d, 0x6d, 0x6c, 0x6c, 0x73, 0x63, 0x20, 0x2d, 0x6d, 0x6e, 0x6f, 0x2d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x20, 0x2d, 0x6d, 0x61, 0x62, 0x69, 0x3d, 0x33, 0x32, 0x20, 0x2d, 0x67, 0x20, 0x2d, 0x4f, 0x73, 0x20, 0x2d, 0x73, 0x74, 0x64, 0x3d, 0x63, 0x39, 0x39, 0x20, 0x2d, 0x66, 0x66, 0x72, 0x65, 0x65, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x2d, 0x66, 0x65, 0x78, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x3d, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x2d, 0x66, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2d, 0x6d, 0x61, 0x74, 0x68, 0x20, 0x2d, 0x66, 0x6e, 0x6f, 0x2d, 0x75, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x2d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x20, 0x2d, 0x66, 0x6e, 0x6f, 0x2d, 0x61, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x6f, 0x75, 0x73, 0x2d, 0x75, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x2d, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x20, 0x2d, 0x66, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x2d, 0x66, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x2d, 0x66, 0x6e, 0x6f, 0x2d, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x2d, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x63, 0x72, 0x74, 0x2f, 0x63, 0x72, 0x74, 0x31, 0x2e, 0x63, 0x00, 0x63, 0x68, 0x61, 0x72, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x61, 0x6c, 0x62, 0x69, 0x6e, 0x6f, 0x6c, 0x6f, 0x62, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x2f, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x2d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x36, 0x00, 0x61, 0x72, 0x67, 0x76, 0x00, 0x00, 0x40, 0x06, 0x50, 0x00, 0x40, 0x06, 0x70, 0x00, 0x01, 0x54, 0x00, 0x40, 0x06, 0x70, 0x00, 0x40, 0x06, 0x87, 0x00, 0x03, 0x76, 0x7c, 0x9f, 0x00, 0x40, 0x06, 0x87, 0x00, 0x40, 0x06, 0x88, 0x00, 0x04, 0xf3, 0x01, 0x54, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x50, 0x00, 0x40, 0x06, 0x70, 0x00, 0x02, 0x74, 0x00, 0x00, 0x40, 0x06, 0x70, 0x00, 0x40, 0x06, 0x87, 0x00, 0x01, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x50, 0x00, 0x40, 0x06, 0x68, 0x00, 0x03, 0x74, 0x04, 0x9f, 0x00, 0x40, 0x06, 0x68, 0x00, 0x40, 0x06, 0x87, 0x00, 0x01, 0x56, 0x00, 0x40, 0x06, 0x87, 0x00, 0x40, 0x06, 0x88, 0x00, 0x06, 0xf3, 0x01, 0x54, 0x23, 0x04, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x50, 0x00, 0x40, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x40, 0x05, 0x98, 0x00, 0x40, 0x09, 0x10, 0x00, 0x40, 0x09, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0xe8, 0x00, 0x40, 0x05, 0xf8, 0x00, 0x40, 0x09, 0x48, 0x00, 0x40, 0x09, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x0f, 0x67, 0x6e, 0x75, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x04, 0x01, 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x4d, 0x49, 0x50, 0x53, 0x2e, 0x61, 0x62, 0x69, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, 0x2e, 0x72, 0x65, 0x67, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x4d, 0x49, 0x50, 0x53, 0x2e, 0x73, 0x74, 0x75, 0x62, 0x73, 0x00, 0x2e, 0x66, 0x69, 0x6e, 0x69, 0x00, 0x2e, 0x65, 0x68, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x2e, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x00, 0x2e, 0x64, 0x74, 0x6f, 0x72, 0x73, 0x00, 0x2e, 0x6a, 0x63, 0x72, 0x00, 0x2e, 0x72, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x70, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x00, 0x2e, 0x73, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2e, 0x70, 0x64, 0x72, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x62, 0x62, 0x72, 0x65, 0x76, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6c, 0x6f, 0x63, 0x00, 0x2e, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x00, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x00, 0x2e, 0x6d, 0x64, 0x65, 0x62, 0x75, 0x67, 0x2e, 0x61, 0x62, 0x69, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x74, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x24, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x09, 0x10, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x09, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0f, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x10, 0x48, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x10, 0x50, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x00, 0x17, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x25, 0x00, 0x41, 0x0f, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x33, 0x00, 0x40, 0x09, 0x58, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x46, 0x00, 0x41, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x53, 0x00, 0x40, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x68, 0x00, 0x40, 0x06, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x40, 0x07, 0x24, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x91, 0x00, 0x41, 0x10, 0x50, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x41, 0x10, 0x54, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xae, 0x00, 0x40, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0xba, 0x00, 0x41, 0x10, 0x58, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x41, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x40, 0x09, 0x58, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x41, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xed, 0x00, 0x40, 0x08, 0x90, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x40, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x17, 0x00, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x20, 0x00, 0x41, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x01, 0x24, 0x00, 0x41, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x30, 0x00, 0x41, 0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x01, 0x3d, 0x00, 0x41, 0x10, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x4a, 0x00, 0x41, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x60, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x76, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0xff, 0xf1, 0x00, 0x00, 0x01, 0x87, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa5, 0x00, 0x40, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc7, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0xce, 0x00, 0x40, 0x06, 0x10, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0xd5, 0x00, 0x40, 0x06, 0x50, 0x00, 0x00, 0x00, 0x38, 0x12, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0xde, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x41, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x10, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x08, 0x12, 0x02, 0x00, 0x09, 0x00, 0x00, 0x02, 0x15, 0x00, 0x40, 0x09, 0x10, 0x00, 0x00, 0x00, 0x08, 0x12, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x02, 0x1b, 0x00, 0x41, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x02, 0x22, 0x00, 0x41, 0x10, 0x70, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x27, 0x00, 0x40, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x4d, 0x00, 0x41, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x13, 0x00, 0x00, 0x02, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x63, 0x72, 0x74, 0x2f, 0x63, 0x72, 0x74, 0x31, 0x2e, 0x63, 0x00, 0x63, 0x72, 0x74, 0x73, 0x74, 0x75, 0x66, 0x66, 0x2e, 0x63, 0x00, 0x5f, 0x5f, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x44, 0x54, 0x4f, 0x52, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x45, 0x48, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x4a, 0x43, 0x52, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x73, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x73, 0x00, 0x5f, 0x5f, 0x64, 0x6f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x64, 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x61, 0x75, 0x78, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x2e, 0x33, 0x35, 0x31, 0x35, 0x00, 0x64, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x78, 0x2e, 0x33, 0x35, 0x31, 0x37, 0x00, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x00, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x33, 0x35, 0x32, 0x37, 0x00, 0x5f, 0x5f, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x4a, 0x43, 0x52, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x64, 0x6f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x61, 0x75, 0x78, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x63, 0x00, 0x5f, 0x4d, 0x49, 0x50, 0x53, 0x5f, 0x53, 0x54, 0x55, 0x42, 0x53, 0x5f, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x00, 0x5f, 0x67, 0x70, 0x00, 0x5f, 0x5f, 0x54, 0x4d, 0x43, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x44, 0x54, 0x4f, 0x52, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x64, 0x73, 0x6f, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x00, 0x5f, 0x66, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x5f, 0x67, 0x6e, 0x75, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x67, 0x70, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x49, 0x4e, 0x47, 0x00, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x5f, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x66, 0x74, 0x65, 0x78, 0x74, 0x00, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x63, 0x00, 0x5f, 0x5f, 0x52, 0x4c, 0x44, 0x5f, 0x4d, 0x41, 0x50, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x00, 0x5f, 0x65, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x5f, 0x5f, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x4a, 0x76, 0x5f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x00, 0x5f, 0x66, 0x62, 0x73, 0x73, 0x00, 0x5f, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x01, 0x74, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x70, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x01, 0x90, 0x00, 0x00, 0x01, 0x90, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x32, 0x70, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x01, 0xa8, 0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x01, 0xc0, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x02, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x03, 0x24, 0x00, 0x00, 0x03, 0x24, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x04, 0x84, 0x00, 0x00, 0x04, 0x84, 0x00, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x05, 0x8c, 0x00, 0x00, 0x05, 0x8c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x08, 0xf0, 0x00, 0x00, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x09, 0x10, 0x00, 0x00, 0x09, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x09, 0x58, 0x00, 0x00, 0x09, 0x58, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x0f, 0xec, 0x00, 0x00, 0x0f, 0xec, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x0f, 0xf4, 0x00, 0x00, 0x0f, 0xf4, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x0f, 0xfc, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x03, 0x00, 0x41, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x03, 0x00, 0x41, 0x10, 0x48, 0x00, 0x00, 0x10, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x41, 0x10, 0x50, 0x00, 0x00, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x4c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xa0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x01, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x93, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x3a, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x20, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x48, 0x00, 0x00, 0x01, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x0a, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x96, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x15, 0x70, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x18, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x23, 0x6f, 0xff, 0xff, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x68, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x78, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xbc, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xbc, 0x00, 0x00, 0x02, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00 }; unsigned char ELF_x64_FILE[] = { 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, 0x00, 0x16, 0x00, 0x13, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xe5, 0x74, 0x64, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xe5, 0x74, 0x64, 0x04, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6d, 0x75, 0x73, 0x6c, 0x2d, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0xd8, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x06, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x10, 0x00, 0x11, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0xf6, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x11, 0x00, 0x30, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x62, 0x63, 0x2e, 0x73, 0x6f, 0x00, 0x5f, 0x5f, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x65, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x00, 0x5f, 0x4a, 0x76, 0x5f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x58, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x35, 0x22, 0x0c, 0x20, 0x00, 0xff, 0x25, 0x24, 0x0c, 0x20, 0x00, 0x0f, 0x1f, 0x40, 0x00, 0xff, 0x25, 0x22, 0x0c, 0x20, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0xe9, 0xe0, 0xff, 0xff, 0xff, 0xb8, 0x2a, 0x00, 0x00, 0x00, 0xc3, 0x48, 0x31, 0xed, 0x49, 0x89, 0xd1, 0x5e, 0x48, 0x89, 0xe2, 0x48, 0x83, 0xe4, 0xf0, 0x49, 0xc7, 0xc0, 0xf6, 0x04, 0x40, 0x00, 0x48, 0xc7, 0xc1, 0xd8, 0x03, 0x40, 0x00, 0x48, 0xc7, 0xc7, 0x00, 0x04, 0x40, 0x00, 0xe8, 0xc2, 0xff, 0xff, 0xff, 0xeb, 0xfe, 0xb8, 0x2f, 0x10, 0x60, 0x00, 0x55, 0x48, 0x2d, 0x28, 0x10, 0x60, 0x00, 0x48, 0x83, 0xf8, 0x0e, 0x48, 0x89, 0xe5, 0x76, 0x1b, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x11, 0x5d, 0xbf, 0x28, 0x10, 0x60, 0x00, 0xff, 0xe0, 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xc3, 0x0f, 0x1f, 0x40, 0x00, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x28, 0x10, 0x60, 0x00, 0x55, 0x48, 0x81, 0xee, 0x28, 0x10, 0x60, 0x00, 0x48, 0xc1, 0xfe, 0x03, 0x48, 0x89, 0xe5, 0x48, 0x89, 0xf0, 0x48, 0xc1, 0xe8, 0x3f, 0x48, 0x01, 0xc6, 0x48, 0xd1, 0xfe, 0x74, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x0b, 0x5d, 0xbf, 0x28, 0x10, 0x60, 0x00, 0xff, 0xe0, 0x0f, 0x1f, 0x00, 0x5d, 0xc3, 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, 0x80, 0x3d, 0x71, 0x0b, 0x20, 0x00, 0x00, 0x75, 0x11, 0x55, 0x48, 0x89, 0xe5, 0xe8, 0x6e, 0xff, 0xff, 0xff, 0x5d, 0xc6, 0x05, 0x5e, 0x0b, 0x20, 0x00, 0x01, 0xf3, 0xc3, 0x0f, 0x1f, 0x40, 0x00, 0xbf, 0x88, 0x0e, 0x60, 0x00, 0x48, 0x83, 0x3f, 0x00, 0x75, 0x05, 0xeb, 0x93, 0x0f, 0x1f, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0xf1, 0x55, 0x48, 0x89, 0xe5, 0xff, 0xd0, 0x5d, 0xe9, 0x7a, 0xff, 0xff, 0xff, 0x50, 0x58, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x2d, 0x36, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x31, 0x7e, 0x31, 0x36, 0x2e, 0x30, 0x34, 0x2e, 0x34, 0x29, 0x20, 0x35, 0x2e, 0x34, 0x2e, 0x30, 0x20, 0x32, 0x30, 0x31, 0x36, 0x30, 0x36, 0x30, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0xc8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0xe8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x28, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x30, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0xc0, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0xd8, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x07, 0x00, 0xe0, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 0x00, 0xf6, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x80, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x88, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x90, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0x00, 0x20, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x11, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x88, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x30, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x70, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xb0, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x80, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0xd0, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x88, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x90, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x20, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0xd8, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0x00, 0x00, 0x10, 0x00, 0x08, 0x00, 0x06, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x01, 0x00, 0x00, 0x10, 0x00, 0x11, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0xf6, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x01, 0x00, 0x00, 0x10, 0x00, 0x11, 0x00, 0x30, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x72, 0x74, 0x73, 0x74, 0x75, 0x66, 0x66, 0x2e, 0x63, 0x00, 0x5f, 0x5f, 0x4a, 0x43, 0x52, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x5f, 0x00, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x74, 0x6d, 0x5f, 0x63, 0x6c, 0x6f, 0x6e, 0x65, 0x73, 0x00, 0x5f, 0x5f, 0x64, 0x6f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x64, 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x61, 0x75, 0x78, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x2e, 0x37, 0x35, 0x38, 0x35, 0x00, 0x5f, 0x5f, 0x64, 0x6f, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x64, 0x74, 0x6f, 0x72, 0x73, 0x5f, 0x61, 0x75, 0x78, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x00, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x00, 0x5f, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x63, 0x00, 0x5f, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x4a, 0x43, 0x52, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x00, 0x5f, 0x5f, 0x54, 0x4d, 0x43, 0x5f, 0x45, 0x4e, 0x44, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x64, 0x73, 0x6f, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x5f, 0x47, 0x4c, 0x4f, 0x42, 0x41, 0x4c, 0x5f, 0x4f, 0x46, 0x46, 0x53, 0x45, 0x54, 0x5f, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x00, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x49, 0x54, 0x4d, 0x5f, 0x64, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x4d, 0x43, 0x6c, 0x6f, 0x6e, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x00, 0x5f, 0x65, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x5f, 0x5f, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x5f, 0x4a, 0x76, 0x5f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x00, 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x70, 0x6c, 0x74, 0x00, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, 0x66, 0x69, 0x6e, 0x69, 0x00, 0x2e, 0x65, 0x68, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x2e, 0x66, 0x69, 0x6e, 0x69, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x2e, 0x6a, 0x63, 0x72, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x2e, 0x70, 0x6c, 0x74, 0x00, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t MACHO_X86_FILE[] = { 0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x24, 0x04, 0x00, 0x00, 0x85, 0x00, 0x20, 0x01, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5a, 0x45, 0x52, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x1e, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x73, 0x74, 0x75, 0x62, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x1f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x36, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x05, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x73, 0x74, 0x75, 0x62, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x1f, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x44, 0x0f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x1f, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x64, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x75, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x1f, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xac, 0x0f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x6e, 0x6c, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x70, 0x74, 0x72, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x6c, 0x61, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x70, 0x74, 0x72, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x20, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x20, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x44, 0x20, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xdc, 0x20, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x20, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x64, 0x79, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x5f, 0xb5, 0x95, 0x0f, 0x40, 0x25, 0x3d, 0x4f, 0xa8, 0xfb, 0x96, 0x48, 0xc1, 0x74, 0x07, 0x90, 0x24, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x0a, 0x0a, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x80, 0x18, 0x00, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x04, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x69, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x42, 0x2e, 0x64, 0x79, 0x6c, 0x69, 0x62, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x20, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t MACHO_PPC_FILE[] = { 0xfe, 0xed, 0xfa, 0xce, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x05, 0xcc, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x5f, 0x5f, 0x50, 0x41, 0x47, 0x45, 0x5a, 0x45, 0x52, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x14, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xb8, 0x00, 0x00, 0xb8, 0x38, 0x00, 0x00, 0x0e, 0xb8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x70, 0x69, 0x63, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x73, 0x74, 0x75, 0x62, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x5f, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x73, 0x74, 0x75, 0x62, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x5f, 0x5f, 0x70, 0x69, 0x63, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x74, 0x75, 0x62, 0x31, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xf0, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xc6, 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x5f, 0x5f, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xf0, 0x00, 0x00, 0x1e, 0x88, 0x00, 0x00, 0xd0, 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x78, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0xef, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x65, 0x68, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc8, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0xef, 0xc8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0xd0, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x6c, 0x61, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x70, 0x74, 0x72, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x54, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0xf2, 0x54, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x6e, 0x6c, 0x5f, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x5f, 0x70, 0x74, 0x72, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x94, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0xf3, 0x94, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x64, 0x79, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x10, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0xf4, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x62, 0x73, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x2c, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x40, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xd0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0c, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x64, 0x79, 0x6c, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x42, 0x3a, 0x3b, 0x7c, 0x00, 0x47, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x69, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x42, 0x2e, 0x64, 0x79, 0x6c, 0x69, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x3f, 0x00, 0x01, 0x73, 0x48, 0x00, 0x00, 0x40, 0x88, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7a, 0x00, 0x00, 0x08, 0x7a, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x08, 0xe9, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x70, 0x4c, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x6e, 0xf4, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x1e, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t MACHO_X86_OBJECT_FILE[] = { 0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xe5, 0x83, 0xec, 0x0c, 0x8b, 0x45, 0x08, 0x89, 0x45, 0xfc, 0xc7, 0x45, 0xf4, 0x01, 0x00, 0x00, 0x00, 0xc7, 0x45, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xf8, 0x3b, 0x45, 0xfc, 0x0f, 0x8f, 0x1a, 0x00, 0x00, 0x00, 0x8b, 0x45, 0xf4, 0x0f, 0xaf, 0x45, 0xf8, 0x89, 0x45, 0xf4, 0x8b, 0x45, 0xf8, 0x05, 0x01, 0x00, 0x00, 0x00, 0x89, 0x45, 0xf8, 0xe9, 0xda, 0xff, 0xff, 0xff, 0x8b, 0x45, 0xf4, 0x83, 0xc4, 0x0c, 0x5d, 0xc3, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x5a, 0x39, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x00 }; uint8_t MACHO_X86_64_DYLIB_FILE[] = { 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xe8, 0x02, 0x00, 0x00, 0x85, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x75, 0x6e, 0x77, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x0f, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x65, 0x68, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x61, 0x63, 0x74, 0x5f, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x2e, 0x64, 0x79, 0x6c, 0x69, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x80, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x38, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x8c, 0x90, 0x46, 0x12, 0x62, 0x53, 0x3f, 0xa1, 0xb8, 0xd2, 0xd5, 0x82, 0x98, 0x48, 0xa8, 0xfc, 0x24, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0a, 0x00, 0x00, 0x0a, 0x0a, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x04, 0x00, 0x00, 0x01, 0x00, 0x2f, 0x75, 0x73, 0x72, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x69, 0x62, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x42, 0x2e, 0x64, 0x79, 0x6c, 0x69, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t DEX_FILE[] = { 0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00, 0x2F, 0x60, 0x9C, 0x3F, 0x1B, 0x04, 0xEF, 0x0F, 0x8C, 0xF7, 0x2C, 0x57, 0xB0, 0xF3, 0x2C, 0xE7, 0xF1, 0xBB, 0xDA, 0xEF, 0x12, 0x00, 0x07, 0x82, 0x90, 0x02, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x5A, 0x01, 0x00, 0x00, 0x5D, 0x01, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, 0xAF, 0x01, 0x00, 0x00, 0xBE, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xDA, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x08, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x18, 0x00, 0x31, 0x4B, 0x31, 0x35, 0xC2, 0x4D, 0x44, 0x64, 0x68, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0x10, 0x02, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x08, 0x3C, 0x63, 0x6C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x00, 0x01, 0x4A, 0x00, 0x25, 0x4C, 0x63, 0x6F, 0x6D, 0x2F, 0x61, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x2F, 0x74, 0x6F, 0x6F, 0x6C, 0x73, 0x2F, 0x69, 0x72, 0x2F, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2F, 0x41, 0x70, 0x70, 0x49, 0x6E, 0x66, 0x6F, 0x3B, 0x00, 0x12, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x3B, 0x00, 0x12, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x00, 0x01, 0x56, 0x00, 0x0D, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x49, 0x64, 0x00, 0x14, 0x63, 0x6F, 0x6D, 0x2E, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x2E, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x79, 0x61, 0x72, 0x61, 0x00, 0x04, 0x74, 0x68, 0x69, 0x73, 0x00, 0x05, 0x74, 0x6F, 0x6B, 0x65, 0x6E, 0x00, 0x01, 0x00, 0x07, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x09, 0x01, 0x09, 0x00, 0x88, 0x80, 0x04, 0x84, 0x02, 0x01, 0x81, 0x80, 0x04, 0xB0, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE1, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00 }; uint8_t ISSUE_1006[] = { 0x20, 0xF7, 0x63, 0x00, 0x6D, 0x00, 0x64, 0x00, 0x2E, 0x00, 0x65, 0x00, 0x78, 0x00, 0x65, 0x00, 0x20, 0x00 }; yara-4.1.3/tests/convention-portable-modifiers000077500000000000000000000012511413423160300215060ustar00rootroot00000000000000#!/bin/sh # pseudo code: # - find all .[ch] files in libyara/ # - grep for non-portable modifiers set -e find . -type f | egrep "\.[ch]$" | xargs egrep --line-number "\%\'{0,1}l[dux]" | perl -lane ' printf qq[- convention: non-portable modifier: %s\n], $_; $single_quote = chr(0x27); s~\%$single_quote{0,1}l([dux])~\%" PRI${1}64 "~g; printf qq[- convention: portable modifier: %s\n], $_; $violation_count ++; sub END { if ($violation_count > 0) { printf qq[- convention: non-portable modifier: %u instances found above; please fix\n], $violation_count; exit(1); } printf qq[- convention: non-portable modifier: none found\n]; }' exit 0 yara-4.1.3/tests/data/000077500000000000000000000000001413423160300145235ustar00rootroot00000000000000yara-4.1.3/tests/data/079a472d22290a94ebb212aa8015cdc8dd28a968c6b4d3b88acdd58ce2d3b885000066400000000000000000000406401413423160300254730ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $f" Z"Ce "Ce "Ce 9 !Ce 9 #Ce 9 )Ce M5 !Ce +; Ce "Cd Ce 9 &Ce 9 #Ce 9 #Ce 9 #Ce Rich"Ce PEL[!  -0pA@P62<P*`01@0<2@.text `.rdata0@@.data@@.rsrcP @@.reloc `&@BUM tDftZ66666CUSTPROF.dllCP_DelItemCP_GetItemCP_GetTaxMapCP_PutItem_DllMain@12N@DUeu4 8Ph  P`TZ4VS_VERSION_INFO! ! ?DVarFileInfo$Translation StringFileInfo040904E44 FileVersion27.1.9.33HProductNameQuicken for Windows8 ProductVersion27.1.9.33: CompanyNameQuicken Inc.f!LegalCopyrightCopyright 2018 by Quicken Inc.TBuild DateTue Jun 5 21:07:13 IST 20188 Build Version27.1.9.33TypeQAfFileDescriptionCustomer Profile Interface DLL: InternalNameCUSTPROF.DLLB OriginalFilenameCUSTPROF.DLLPA PAPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDINGXXPADDINGPADDN0`00011#1,1f111,252B2Q2V2a2f2q2v222222222222222 3.3;3@3f3o33333333333344/4G4_4e4x4444444444 5575D5\555$6Q6_6j6p666666677>7C7b7h78 88=8Q8W8888888K9P9g9999999999::::$:4:::@:F:L:R:Y:`:g:n:u:|::::::::::::::::; ;!;(;0;6;<;B;H;N;T;Z;`;f;l;r;x;~;;;F*>N>i>u>}>>>>>>>>A?M?U???????? 00031D111111110 011D1H111124282@00 0$0(0,00 *H 0}1 0 +0h +7Z0X03 +70% <<<Obsolete>>>0!0 +"C-I+M00W~|NYKw;0  *H 01 0 UZA10U Western Cape10U Durbanville10 U Thawte10U Thawte Certification10UThawte Timestamping CA0 121221000000Z 201230235959Z0^1 0 UUS10U Symantec Corporation100.U'Symantec Time Stamping Services CA - G20"0  *H 0 ITK %y"W*oܸ&Csk¿.PZvC%CE{t"״MD$k_E;DCsi+˙r&Mq1QaSI,xE/W?=ƒJ{3y uAQlie)`; tޒ"t|'JÞ-'}aqPK],e ؖ|NHDD h]jxdE`F~T|yq00U_n\t}?L.02+&0$0"+0http://ocsp.thawte.com0U00?U8060420.http://crl.thawte.com/ThawteTimestampingCA.crl0U% 0 +0U0(U!0010UTimeStamp-2048-10  *H  yY0h O]7_R DnmX|0i#s oG9*ÎY M1\*zzWLey@b%n7j!AW?wI*^8j"Q~0085njP0  *H 0^1 0 UUS10U Symantec Corporation100.U'Symantec Time Stamping Services CA - G20 121018000000Z 201229235959Z0b1 0 UUS10U Symantec Corporation1402U+Symantec Time Stamping Services Signer - G40"0  *H 0 c 9D#DIa Sۭ,Jn"hcSit<üu00!C$Vt0  *H  01 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA0 170412000000Z 190604235959Z0|1 0 UUS10U California10U Menlo Park10U Quicken, Inc.10U Operations10U Quicken, Inc.0"0  *H 0  X6h1"HG,hM.~oJٓߠMM=Ɨ'p@A,B T΃Qjp[돼`i *0=dtWʫ/H;" ;3T^AlG)1uk4S'lsjW x# ҼsP+i#*Stk<](Ƈs%#h,ka*)WyJ#.lGh8hv/>0:0 U00U0U% 0 +0aU Z0X0Vg 0L0#+https://d.symcb.com/cps0%+0 https://d.symcb.com/rpa0U#0;Sy3}.+ʷrf0+U$0"0 http://sv.symcb.com/sv.crl0W+K0I0+0http://sv.symcd.com0&+0http://sv.symcb.com/sv.crt0  *H  (Ad ҙOŔ?@Ze6NR'5AFB=x`n[QKG*; ntb(HyO(.^ qL˜G :_ø,a.vXKDxt\r[>&5 Vh&I\C{!!/{svtAkm^ŶĴt~Dxہ#.P[X`e-9 6`:5U\"%ze00Y0A=xvI`a}ʆ*0  *H  01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CU٦V?.)|=꺓P")L:_֤%k/L'{ "`?MLrgw'Ǻ5I(J D 6+P]'KT+^t É"wCL?d!1<08001 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA!C$Vt0 +p0 +7 100 *H  1  +70 +7 10  +70# *H  1UUr;lE* .20  *H 3W$ԿhQ {!XT,pQ;c%xsqDߨm>$gkuOQӓ=dcS!my,y9$Б |(0 VHZȻM5@NbxF #li!ܳ _a %u/}5q]&(/>>rB*z`QHH@h56T+Oѕ>īLUYuWtpbҚ4aOot$NG 0 *H  100r0^1 0 UUS10U Symantec Corporation100.U'Symantec Time Stamping Services CA - G285njP0 +]0 *H  1  *H 0 *H  1 180605163551Z0# *H  1Lem>N0  *H E 0G$H[W5YUwa3;HG{V8['j O6cQnHPH )I 9)YDg#>m6z8/BlXSn W 3 ݐ) &AxxU`LMN

v8|ެTCFM9k_(4t  o]T\u A+Ĉ1G2P'ߏ&j&!lonT3\1>U0 ;29nkD;tu.7ܥ ;tW;v-XZ  N 'u$_ fWP/R5SjuCavO4!ht.,(Px%mFVvMPQ909áM=LS'3V-9t݋Ț +@I^x@L0;J4n7ƍ=SW 1=HىMS VDS4VN-^kxJ~܉UFk)Mm} }$u̫ -luDsPQhWmLmP`8U+^*d2¥½ ̍4z6_ Ъ|H ]URQȅhG8+׾|MQs"kuxA W.x 2!-h~ظW$K>&~ftBK'jPpyNѨ 6-A$ uu~9^$t-t(G<uc78%;AØZj71GKK^~PwCk<aVEGhs|EQ/ mA@|UIB +J;u nrw!:BFy]`۷(J O un9Wt,w9h}A9QڗP },ۉ/!rQɄ8Y )Sv]p].VrمC}9G O;ώ[=}?t_Ky'_q=AВzsiA:iυ5+NJAJm&]˃S}b[ENڏ'G}qӅ3 I_3 LHFU"4*64Ͳi 54MӠp\B]4M,?l5:?4[zi`VN@6ie3și^QDB.dllcM&E0'JP\HE0AH[RSDSEt 70JK\RoD:\workspace\2018_R9elBld\target\checkouustp'{rof\ease.pdby\9"eu ';avlۯ0<@|2nW 425tf`_ig"y53UȨA rn ޙD%Ueu*i4+U JFQU%Tɨw@T SleepLoadLibraryA GetL .astErrorFr"|PcAddss8cal!= All IsDebuggoerP'enti9UnhandqdExcutionFiltecsmpCuBwsdTmina$Sy`oemTimeAs;e =IdTh߂1ckCouEncod^MXoeIukgiRai$Tmpl-5  :<\.    ! &    (!  S-H-   m=!gG M#  )Kߺ ^ 6/Wmq- $O+ 9Q 9k @3T@] WL[-!  )u -0F@l I9pevoɄA@4@qP6[<Pu`Ew =81ml/<2.`%xt1`.rdºv'_@.&MS'OsrcvhOl Ŧl&'Bbq2@|$`pW FGurusu s1Ƀr Fttuuu Ausu s/vBGIucwL^9G,<w?u_f)ٍ` t<_0ąPGt܉WHU ta1 ^1G t" PA%B2P`KERNEL32.DLLMSVCR100.dllGetProcAddressLoadLibraryAVirtualProtectfree[–l>ZϖږCUSTPROF.dllCP_DelItemCP_GetItemCP_GetTaxMapCP_PutItem_DllMain@12 02 2 02 20 *H 0}1 0 +0h +7Z0X03 +70% <<<Obsolete>>>0!0 +"C-I+M00W~|NYKw;0  *H 01 0 UZA10U Western Cape10U Durbanville10 U Thawte10U Thawte Certification10UThawte Timestamping CA0 121221000000Z 201230235959Z0^1 0 UUS10U Symantec Corporation100.U'Symantec Time Stamping Services CA - G20"0  *H 0 ITK %y"W*oܸ&Csk¿.PZvC%CE{t"״MD$k_E;DCsi+˙r&Mq1QaSI,xE/W?=ƒJ{3y uAQlie)`; tޒ"t|'JÞ-'}aqPK],e ؖ|NHDD h]jxdE`F~T|yq00U_n\t}?L.02+&0$0"+0http://ocsp.thawte.com0U00?U8060420.http://crl.thawte.com/ThawteTimestampingCA.crl0U% 0 +0U0(U!0010UTimeStamp-2048-10  *H  yY0h O]7_R DnmX|0i#s oG9*ÎY M1\*zzWLey@b%n7j!AW?wI*^8j"Q~0085njP0  *H 0^1 0 UUS10U Symantec Corporation100.U'Symantec Time Stamping Services CA - G20 121018000000Z 201229235959Z0b1 0 UUS10U Symantec Corporation1402U+Symantec Time Stamping Services Signer - G40"0  *H 0 c 9D#DIa Sۭ,Jn"hcSit<üu00!C$Vt0  *H  01 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA0 170412000000Z 190604235959Z0|1 0 UUS10U California10U Menlo Park10U Quicken, Inc.10U Operations10U Quicken, Inc.0"0  *H 0  X6h1"HG,hM.~oJٓߠMM=Ɨ'p@A,B T΃Qjp[돼`i *0=dtWʫ/H;" ;3T^AlG)1uk4S'lsjW x# ҼsP+i#*Stk<](Ƈs%#h,ka*)WyJ#.lGh8hv/>0:0 U00U0U% 0 +0aU Z0X0Vg 0L0#+https://d.symcb.com/cps0%+0 https://d.symcb.com/rpa0U#0;Sy3}.+ʷrf0+U$0"0 http://sv.symcb.com/sv.crl0W+K0I0+0http://sv.symcd.com0&+0http://sv.symcb.com/sv.crt0  *H  (Ad ҙOŔ?@Ze6NR'5AFB=x`n[QKG*; ntb(HyO(.^ qL˜G :_ø,a.vXKDxt\r[>&5 Vh&I\C{!!/{svtAkm^ŶĴt~Dxہ#.P[X`e-9 6`:5U\"%ze00Y0A=xvI`a}ʆ*0  *H  01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CU٦V?.)|=꺓P")L:_֤%k/L'{ "`?MLrgw'Ǻ5I(J D 6+P]'KT+^t É"wCL?d!1<08001 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA!C$Vt0 +p0 +7 100 *H  1  +70 +7 10  +70# *H  1UUr;lE* .20  *H 3W$ԿhQ {!XT,pQ;c%xsqDߨm>$gkuOQӓ=dcS!my,y9$Б |(0 VHZȻM5@NbxF #li!ܳ _a %u/}5q]&(/>>rB*z`QHH@h56T+Oѕ>īLUYuWtpbҚ4aOot$NG 0 *H  100r0^1 0 UUS10U Symantec Corporation100.U'Symantec Time Stamping Services CA - G285njP0 +]0 *H  1  *H 0 *H  1 180605163551Z0# *H  1Lem>N0  *H E 0G$H[W5YUwa3;HG{V8['j O6cQnHPH )I 9)YDg#>m6z8/BlXSn W 3 ݐ) &AxxU`LMN

mscorlibSystem.ReflectionAssemblyTitleAttribute.ctorAssemblyDescriptionAttributeAssemblyCompanyAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyTrademarkAttributehpjsoaputility.Sv.resources.dllhpjsoaputility.Sv.resourcespthpjsoaputility.XmlStreamSoapExtension.pt.resources 9d7fC|o~z\V4r712|AwX $$RSA1-mb몂&(a=++]FN ´*K$&6!yso{LF m9 a5zh^`}Yz'\Q%`r^1AXO+ŀ0%N% @%_CorDllMainmscoree.dll% @0HX@4VS_VERSION_INFO00?DVarFileInfo$Translation$StringFileInfo000004b0,FileDescription @FileVersion1.0.4400.37258` InternalNamehpjsoaputility.Sv.resources.dll(LegalCopyright h OriginalFilenamehpjsoaputility.Sv.resources.dllDProductVersion1.0.4400.37258HAssembly Version1.0.4400.37258 `5yara-4.1.3/tests/data/33fc70f99be6d2833ae48852d611c8048d0c053ed0b2c626db4dbe902832a08b000066400000000000000000004143041413423160300252430ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PEL|N 2DP `@ -YPK?z  H.text0 2 `.sdata`6@.rsrc?@8@@.reloc x@BPH8zD 08& ( :&}8.  E1 P8& 8( 8} 8(( ( 9& 8*0;8& 8t! 8( 9 8(  (( 9+ :&*( : 8( t$o l 8r(  9`& ((  8& 8?8( 8'*( t$o l8~  E#-W/iN}0H {r> ( :`&((( 8B(  (( 9 ( :&((<" 88@ (  8((( 8* 8( : 8{ 8t! 9~& *{@ "( :_8Z8( 8@( t$o :g 8!(((( ( 9& 8( t$o 9 !8((( 8{@2 :&( :p 9&*((? 8l8 ( 8T EE :2&*( t$o 9V ( : 8( t$o : 8*(  ( :& e**>8&}*:8&{*>8&}*:8&{*:8&([*F8& ( *&8&*&8&*F8& o *F8& o *Z8& ( *F8& o *F8& o *V8& o *F8& o *V8& o *J8& ( *N8&((*:8&([*F8& ( *&8&*&8&*038&{  ( t |(+ @*038&{  (" t |(+ @*08&(-9n& (-:)&(# 8(+8  E ! 2 8(,8 & 9&($ 8*~8&(% 9 {& (' *>8&(( *08& (.9K&{(/(2> 8*{)  o* o+ 8P  E S0}QFq1 y& nB% 8q{ (3} 8V{ (3;o 9:&{{)  o* o, (8 8 {(/(2> 88{ (/(0u 8& (-:&{) :o* o, 88>X 8{{)  o* o, (8 (-:V&{{)  o* o- (9 :)&{?{(/(2?8(5{(s. (6 8 (.:& 8{(7 (-:&{{)  o* o- (9 8v{(7 8`{?{(/(2?8(5{{) +:o* o- (9 8{{)  o* o- (9 8{(s. (4 8 o/ (19 8{)  o* o+  (-:s&{ 0 s. (4 (.9L&{ (5 85{{)  o* o- (9 8  8 8{ (/(2? 8{ 0 s. (6 8{{)  o* o- (9 8*08& 8!{) :o* o1 o2 8{)  o* {(:o3 8{)  o* {(:o3 8{)  o* {(:o3 8i{)  o* {(;o2 (-:9&{)  o* {(3o3 8  E Z*6} 8{)  o* {(;o2 8{) +:o* {(:o3 (.(-9n& (.9G&{)  o* {(:o3 9&{)  o* {(:o3 8& 8{)  o* {(3o3 8*:8&{ *&8&*:8&{*>8&}*:8&{*>8&}*:8&(4 *08&(-(.:v& 8E{)  o* o, 8#{(=@y8.  E&F8!& 8{(< 8{{(>(8 8*8&{9 {o5 *08& 8S{{ (3;j(.(-9P& 8#{ {(58*  E!8 & 8(? 8*08& 8i{908<& 8N9(.(-9& 8.(6 8{(@8  Evv 8*08& 8 { $((F : &{ X((F 8 {(G 8 {  ((E 8v {  ((E ,8V { (I{(J 86 {(T I8  J((X a(-: &{ ((F 8 s7 }  9 &{ ((E 8 { d s8 (Q 8 { s8 (Q /8k { (G 8U { &((F 86 s7 } 8! {(I{ (J 1(-: &(As9 38 s: } (.: 8 { (I{(J %9 &(Y `8 {([ o8y s; }  :c &s< } 8M s< } .88 { s8 (Q \(.: 8 s< }  L9&{d s8 (Q ^8{ ((E 8{(G +8{  ((E C8{ (I{(J {8b(] 8Q{(G (.96&{  ((F J(.:8 {(P ?8{ ((E b8{ (I{(J F8{  ((E 8{ (I{(J(.:~& 8j{(P :S&{(G 9;&{(V 8${ ((E '9&{ l((E !8{ ((F -8s= } z8(U 8{ D((E G8}s7 } 9g&{ d((E :E&{ (I{ (J :#&{ s8 (Q |:&{  ((E D(-:&{ (I{(J r8{(9 ~8{ p((E (-:~&{  ((F 8^{ ((F 8?{ (((E (.:8{ (K v8(W{(J m8{ ((E 9&{ D((E 8{ ((E T8s7 } 88m{ ((F 8N{ (I{(J =8.{ (I{(J R8{ (B 48{P s8 (Q :8{ ((E x8{ ((F y8{ ((E d8|{(I{(J *8\{ ((E 8<(C S(-:'&s> } 8{ (\ 98{ (I{(J 9&{(G ;8{ H((E t8s? }  l:&{ (I{(J (.:h8c{ s8 (Q 58B{ ((F O8#{ (M 8  <((E8I& 29&s@ } 8{ (I{(J (-:&{(M 9&{  ((F p8x{ ([ 8b{(\ [8M{(B N97&{  ((F <8{ ([ K8{ &s. (H 78s= } 8{ (I{(J (8s= } f8{(I{(J g:u&{)s. (H A8Ss= } 8>{(G V8({ (I{ (J8k  EZ)%VREdh>[m$d5?='yLx/- }@.-;VN,wa!U ? zC+Y WAgcNV@+m -v0gBnUjbv9 ]8Y{(M "8C{ d((F k8$s. (Z i8{ <((E M:&{(D s8s< } 8{ (S >8{(L &8{ F((F )8p{(G :Y&{ ((F E:8&{ ((E q9&{ t((E 08{ ,((F (.:8{  ((F :&{ T((F (-:&s? } 8q{ (L u8[{ (R h8E{ ((E 8%{(G c:&{ (I{(J 8{  ((F #8{(D @8{(M U8s< }  8{'s. (H }(-:g&{(9 w8P(W{(J :4&s< } W8s= } 8 { (I{(J 8{(I{ (J 8{ ((F Q8{ J((F 8{(L 8u(W{ (J n8Z{  l((E B99&s> }  8#s< } $8{ ((F 9&{ ((F H8{ (B 8s@ } :&{(G e8s> } Z(-:r&{(K 8[{ s8 (Q Y(-:5&{ (I{(J (.:8 { \((F 8{ (N(O X8{(I{ (J :&{ ((F j8s= } 8{{(L 6(.9`&s> } _8J{ (I{(J 8**:8&([*&8&*&8&*F8& oA *V8& oB *Z8& ( *F8& oC *F8& oD *V8& oE *V8& oF *V8& oG *V8& oH *V8& oI *V8& oJ *F8& oK *F8& oL *V8& oM *F8& oN *F8& oO *V8& (P *F8& oQ *J8& (R *F8& oS *F8& (S *V8& oT *f8&  oU *V8& oV *V8& oW *V8& oX *F8& oY *V8& oZ *V8& o[ *V8& o\ *V8& o] *:8&(^ *V8& o_ *V8& o` *V8& oa *V8& ob *V8& oc *V8& (d *V8& (e *V8& (f *F8& (Y *V8& (V *V8& (g *V8& (h *V8& oi *F8& oj *V8& (i *N8&(i(j*08&(k(l: & 8\~!(m9y8& 8<! 8,rp(nok sl 8  E (l9&~!*68&~"*:8&"*8&(_ d(~"(o*8&(_ (~"(o*8&(_ (~"(o*8&(_ (~"(o*8&(_ (~"(o*8&(_ (~"(o*8&(_ F(~"(o*:8&([*F8& ( *&8&*&8&*Z8& (m *J8& (R *f8&  on *08&(}(~:d& 8(|84  E / 8(z 8({8& 8*08& 8)(s 8(z8X  E&7M \ 8(|(}9!& 8 p(( 8v& (}:&({ 8*08&(}9z& 8M( (~:984{+( 8{+(8*  E8& 8*08& 8{+(8*  E!K8 & 8( :&{+(((}(~:& 8*V8&(&{#*>8&}#*>8&}#*>8&}#*>8&}#*08& 8{$(8.  E'H^8 & 8( (}:&{$9(}9& 89 (~9&*08& 9J&{' (( 38*{%(( (}: &{'es8 ( /8{+ Ts8 ( 78 n(( *:&{(( 9&{& (( 9w&({%( =:Z&{)( 8C{* H(( 8$s7 }' 8{&({'( 8s7 }* B8{&( >8( 8{(( #8s7 }( 8{) >(( 58k{'( 88U{% (( G86{)Es8 ( ?8{*( D8{'xs. ( !8( H:&{* V(( '8{& (s8 ( 8{*( $8y( %8h{( (( 8I{&({(( ,8){(ws. (85  EIOo@g MaoO. 'OO&8"z;HVl7!(~?#e-d^@ 8{+so ( 8s7 }) ;8({+( 48( 8p{*Es8 ( (~9M&{) .(( 8-{*h so ( (:&{+( 8{*vs. ( 8sp }& 08( 6(}:& ~s8 (8& E8{&({*( <8h{+ ^(( &9H&{(es8 ( 28){' (( .8 {%Uso ( 98{+( 8{' ( so ( -8{%( F8{'( 9&sp }% 8s{&( 8]{(  so ( :;&{)us. ( 8s@ }+ C8( 8({&( 8{) so ( 8( )8"@"PAsq ( ":&{&( 8r{)( (~9W&( @8E (( A(}:&&{( (( +8{&({)((}(~:N& 8{% s8 ( 19&{&Vso ( 8*:8&([*F8& (r *&8&*&8&*Z8& (s *V8& ot *V8& ou *:8&(v *:8&(w *F8& (x *F8& oQ *V8& (y *F8& oS *F8& (S *:8&(z *V8& o_ *V8& o{ *V8& oV *V8& o| *V8& o} *F8& oY *V8& oZ *V8& oI *V8& oW *V8& oX *V8& o~ *V8& ( *V8& (e *V8& ( *V8& ( *F8& (Y *V8& ( *V8& ( *V8& ( *V8& (V *V8& oi *V8& (i *F8& (j *08& !8{4s o (:!& 8(s }=8& 8( 8{8s o 8a +j}> "9J&~ {7s }8 8)s}6 8{8% s s }5 8{ o 8{4s o 8s }< 9&( 8( 8~{i(( 8d{4s o 8Cs }? 8.( 8s}48 ! E$W@=B4 "pUMav%( (9\&s }; 8F{Q{6( #8+{8s o 8 s }: 8( 8s }7 8{ {5o 8{=s o 8{8s o 8s( 8c(( 8Nr?p}@ 98&{=s o 8{ {4o 8( 8*08& 8~ o 9~ (o {7o ݺ ~ o 9a~    (o (  ( (o*  0 (((o D ~ o 9/~ 6 (o* N (((o ~ o 98F  E Ng=08& 8~ ((((o 8( 8y~ o 9 8`~ o 9 8G{ { o o :&&~ (o 8~ (o ((9"& 9&~ T (o 8{ ~ (o 9&*3Dvv3Dw08&((:& 8U~ o 9. (97&~ (o 8(8f  E 4M 8~ (o 8& (:&~ o 9 8*0 8& 8~ o 98q& 8{Q((?s(9& 8~ (o 8X 9& 8r~  (o 8T8w{Q(((  o* X ( (o+ (8  E dM=wY 8{6(  o* o- :8o{6(  o* o+ o 8g~ o 9 9M&*0 8& :&(  o* {6oo3 9&{79l 9u&~ o 9 8[~ 2 (o (98&~ o 9 (:8} 8& :&{ o 8(  o* (o3 8X (9& (:&{7o 8 8y8L(  o* X ( ({Q(((o3 8({Q((?8  E x*T6$HZ;x 8(  o* {6o;8o3 8g( 8U~ X (o 87*08& (:5&{L(  o* o (8  E-F Yj % 8{Ls. ( 9&~ o 9 8}( :k&}A 8Y~ ~ (o 8;{Ls. (8& 8}A 8{4( o &((:& 8~ o 9* 8~ (o 9&*0\ 8& 8( (o o 9&~ o 9 8( (:k&o (:T8O~ o 9z 86~ (o 8s 8  E !h]h s<CU#! 8~ o 9 8h (o (:I&( ((& 8.~  (o (:R& 9&~ \ (o 8*( 8{5~ o 8.& 8~ (o 8o 8~ o 9 8j{8(  o* o s { o ( 8#8X( ((& 9&{?o 9 8{?o : (:&( 8{5o 8o 8{5o 9x&*0R 8& (9&~ o 9 8o :&{?o :m :&{5o 8( ((& 8~ o 9 8f( (o o 8@o 8/( (:&s 8   Es\%$Q\jE K =Wh  8{5~ o 8g~ o 9 8N*(8)& 95&o 8#~ (o (9& 8~ & (o 8~  (o 9&8( ((& 8{?o 9 8{8(  o* o s { o ( 8:~ o 9 8!~ J (o 8 (o 8{5o (:&( 8*0  8& 8M( 8<#?CH :!& ( 8  (( (; 88 ( (( (( (9&{;o j[l 8  8z( j[l 8_#?C 8F ( :2&#@[ 8~ (o 8  ( 8(s (:T& 8(  o* o,  8  8~ o 9- 8p8 l ( r (( z (( 86 ( 8#  ( (:&  ( 8 R ( X (( ` (( 8#@[ 8#@[ (:& ( (( (( 8P  8A#@[ 8)({;o <8  E v.J5bRn?E: +X[#8& 8k ( 8X**0 8& :&( ((& 8~ (o 8~ o 9 (:c8^{@s ({9(  o* o+ o { o > 8 !8{?o 9 8{?o : (:&( ((& 8~ (o "8~ o 9% 8{~ h(o 8]{?o 9 %8B 86( &8%8{;o : 88{?o : 8~ o 9 (9&( ((& 8~ o 9> 8{@(9 #8z: (:e8` (:O8J 8>{@(9 8${;o {<o X{9(  o* o+ o { o ? $(:&(((& 8~ (o 8~ &(o 8w~ o 9 8^~ 8(o 8@8( ((& 8!~ o 98Z  E'Y-d)yK=>:Wr ;! 8U (:D&{?o :*8@& 8"{?o : (9& 8{?o 9 8*0X 8& 8o 9&{;o o ((9/ 8{;o o 9 88(  o* o- o ((9& 8Qo 8@o (:*&{;o o 9 $:&~ h(o 8~ o 9 8(: (:&{;o o ((9 8(  o* o, (:(  o* o, 8(o (  o* o+ o 8o 8[  E&"uX  S@u3z#\:eGJ :X&s #(9B&o 80{;o o 9 8{;o o ((: 8{;o o ((9 8{;o 8(  o* o- o 8so 8b{;o @ 8G8T(  o* o- o 8o 8 (  o* o- o (:&o 8(  o* o- 9 !8o 8(  o* o- o 8& 8i(  o* o- 9} 8C{;o o 9 8#(  o* o- 9 8s 8(  o* o, o %:& (o :&*08&(9& :&s (9k&~ o 9 (:L8G{@o 81o 8 (  o* o, (:(  o* o, 8(o (  o* o+ o 8o 8& 8 o 9& D(o 8n(  o* o- o 8H~ (o 8*o 8o 8  EU#x 7QwU? 8*0 8& 8~ o 9{ 8 (o 8o 8o~ b(o 8Qo 8& 8:o 8)s 8o 8>  E O~5~`(:& (9&*08& (9F&~ ((o 8!8 B(( & (:&~ ((o :&((((9& 8{7o (9 (:&*(  o* o2 8h (( 8M \ ( (::& 8#((:8  E;V* ^;a0 8~ (o :z&8Q (%X ( ( (:G8B(  o* o, 8 ( (9 &~ o 98& :&{7o 8~ o 9 8 ( 9& 8{7o ( : 9t&*0Z8& 8"{5~ o 8  E2L59|Tp u@3"(5t8l&US#WqY )8)~ o 9 -8{5o 8( (:8*{?o 9 8~ p(o +8( 9&(9c 8{{?o 9((9U& 8P{5o (:681~ (o ((:&{5~ o ,8 .( #8~ <(o 8~ (o 8( 18~ (o .8q{5o 8[~ o 9 (:=&{5~ o 8"( 8(8d& 8~ o 9q 0:&~ o 9R /8( 8{5o 8{8(  o* o (s { o ( 8V( 8D(: 8/{5o 8*{?o 9. !8{@r?p(9 8{5o &(9& '(:8*( "8 4( 8( 9m&*~ o 9 %8R{8(  o* o (s { o ( (:&~ o 9. 8~ (o (:8{@r?p( 9 *8( 8( 8{@r?p( 9 9_&(( (@ (:681{8(  o* o ((s { o ( $(:&*08& (:>&~ H(o (:&(8  ES55br (9&~ o 9(9=& 8~ (o 8~ o 987& 8i*08& 8I(  o* {L( o3 8(8`  E iEY* E :&~ (o 8~ o 9Y :&~ (o 80& 8n~ o 9((9& 8E*08& 8V{O(  8@( :o* r?po (:&{O(  8{O( 8X 8& 8( :o* {O(o2 9&s }O((9& 8( 8p8{O(( 8N{O(i? 81 :$&{O(@8  EKe{;m& (:&{O( 8*0 8& (9#&{j(b(8`  E'@Uo WpA 8{j(8& :&{j( ( 8v{g( 8`{g( :I&( (:3&{P(@ (:&{P(( 8( :o* {P(o2 8{P( :o* r?po ( 8((:& (:x&{i( 8b{g( (:H&{i( :0&*0;8& 92&{d(" 8( 8   E&$b- 2CV5n5wPH :^Y 8Y(}@ 8C{;o 8.(((:& !8 {[(# :&( (!& 8(@ (:& (( $8( :o* ~ o (9z&(8& 8c{@( 8K{@(( 8.( +:o* o3 %9&#o*  8( (!& 8(  (:&( (!& 8(l 9&( #8}{\(# 8g{@s :P&s 8?( (:*&{Q($ 8( 9&{Q((( 8{G(" 8( :o* o2 9&( (:&(  o* o3 8h{F(" :Q& 6(( 86{](# 8 {a(# "8 *08&((9& (:*& f(r?ps 84  E"." @ 8{:(% :&r?p( 8& 9&*08& (:6&(( 8*{:('&8*  E:8& 8{:(&:((9& 8*8&{:(&9{:((to *r?p*08&((:!& 8f8.{:('&8:& 8E (:4&{:(&Y? 8X 8  Ey 8*0A8&()9(9& 8"8P(9E8  E &H&zY` 8{Q(*& (:&{;{;o o 8( 8~(8& (:b8]{;{;o o (:6&& ~ (+o (c((&*A&w0)8&(:|& 8o 984  E&2& B 8o o (:&o o 8& 88!o ((:W& 8.(,: 8(8&  E8& 8 9(-* 08& 8(8*  E6Z8 & 8{]{;o (# 8(((9& :&*08&(:& :&{6{6o;8o8{6(.o 8c{Q(/ 8N(.{6o@8& (:#8{6o8  E9| 8*08& 8s: 8c{Q(2(3(5{:(&s 9/&*{Q(2(3(49Z8p  E9=iy+ D4S 8{Q(2(3(6(7(89 8n*(r?p(9D((9F& 8>{Q(2(3(4: 88{G("85& 8{: (% (:8{Q(2(3(5 x((9 8{G(" 8*o 8|( 8j{F(" 8T{;{Q(2(3(4t$o o 8( 8 {Q(0(1; 9&r?p( (:&{F(" 8*0V8& 8{M(" 88(8F& 8{Q((;9 8i 8]( 8MX :>&*(9~ (:t  8i?q8Q  E z9/j! ?^O 8(r?p( 9l((9& 8{d(" 8x*08& (:0&*(= 8 (=8*  E;8& 8(9~ (<9(:& 8*08& 9&(8R  EFM( e4{`8 & 8*(> @) 8(r?p(9 (:o&{G(" :W&(>@ 8@{F("(9s& (:&{Q(0(1{Q((;@2 (9&8{G(" (:&*( 8( 8{F(" 8( 8u*(>.@ :\&*0i8& (:&8(At! 8c8(,: (:D&(6(7(B9((9A& 8 8*  E8{& 8uF 9 (-{\(# 8(r?p( 9u (:8{Q(0(?:K 8{Q(2(@ 8& 8{Q(0(19 8l* 8_{a(# 8I{]{Q((;(# 8{\(#8B  E ll@0_ ((9&& (9&{a:{Q(0(18(#*08& 8*{Q(2(3(6(7(B9 8*{Q(2(3(4t$ 8(Co 8{Q(2(3(5o (:& 8:" 8o*o o 8X*s 8G(h((& 8-{@r?p( 9 8*{Q(0(1: 8(G@U8& 8(J (((@(& 8o  %Ё(H(I> (:t&(Fo (9Y&o (: (:9&(Do (:&(Eo 8  E4Oz` 8o(O 8( 8*08& 9M&{G(" 86X 8({Q((;<8<  E9)g/eOe IC0#YqE 9&{[(#(9S& 9`&8L(At! (:o& (49$((9v& (:D8?(,: 8){; (4t$o  &8*  Eu8& 8SuF9(-{Q((K(6(7(B98 8V( 8F{Q(0(? 8*{Q(2(@ 88r?p}@ 8{a(# 8*{@r?p(9 8*{Q(*& 8{Q(0(1: 8(g((& 9m& 8`{F(" (:E8@8i(At! (9& 8 8:  E-B-R (:&8=(,: :&(6(7(B98& 8uF9(-9 9g&{d(" (9K&{Q((;? 8*{Q($8& 8{Q((K(L 8{Q(2(@ 8{Q((K(6(7(89V 8{b(# 8*08& 8{d("8  E gg# EQ%5K 8{Q((M 8{a(# (:o8j 8^( (:I&*{@r?p(9 8({F(" 9&{G(" :&8(At! ((:A& 8 8*  E1F8,& 9&(6(7(B9 8(,:m 8uF 9 (-9 (:(8#{;o (: &{b(#85& 8(g((&((9& 8{Q($ (:&8r?p}@ 8( :{&{[(# 8d*708&((9& 8*(N:i 8r?p}@ (:f&( 9T&( 9B&{Q((;9 8"*(O 8(r?p( 9 8*{@r?p( 9H 8{d("8n& 88e(Q ((:D& 9&(8&  E%8 & 8(R: 8uF 9 (-{M("8  E^Cp.  8(P 8*0o8& :&{@r?p( 9$ 8c(To 8& 8Ho (: 8. (( 8( 8*( 8o  %Ђ(H(I? 8 ((  8(S: 8(J (((@(& 8i(UY(V\;! 8J*{M(" 83o ( 8r?po 8  Ep![fYK@ Qu0:| 8*s 8(Do 8s(Co (9 & 8R(Fo 8=(G@ 8'*08& 88n(At! ((9H& (:&(X8&  E%8 & 8(,: 8uF 9(-{Q((K(4:8_  E e%{{ 8{Q((M 8{Q((K(5 ((9r(9=& 8n*{Q(W 8X{Q((K(X8*& 81{Q((;: 8{Q(Y :&*0u8& 8Y{;o o (( $80{< o o ((8+  E.+1-7`{*JaD:.H Vz )A]M 89{;o o  j@ !8(X 9&{;o ? 8o (]?z 9& X (8( {< o o ((!&((:& 8}o o  o (( 8V{c(# (9;&{;o (\ 9& {<o ?| 8(L 8( {;o o ([  #o* (!& +9&r?p(( 8(X 8{Q( &(( 9\&( {;o o ((!& 8,{Q((Z 8( o o  o ((!& ':&( {;o o l( (!& #9&r?p( 9 (:&8s{Q({< o o ( 8`8u{Q(o o  o ( 9.&(7(^ :&(7(^8& 88 (:&( {< o o ([#o* (!& 8{;o *8( o o  o l( (!& 8a :T&( o o  o ([#o* (!& 8{< o (\ %88{Q({;o o ( &8X )8( {< o o l( (!& 9&o o  (\ 8l: 8[{@r?p(9: 8<X ,9-&(L :& "8 {c(# -8*08& (9&~ n(o 8e~ o 9 8L~ .(o ((:W& 8{ o 86  E r))T>8 & 8~ o 9 8{ o 8*0{8&((:S& 8~ (o 8~ (o 8~ o 9 8~ o 9 :~&( 8l{A9 8W{Ls. ( 95&{L(  o* o (8L  E 3_N 3!t7 8{Ls. (8(& (:&*08& 84~ o 9 :&(  82  E^@}8Y& 9&~ r(o ((9& 8~ (o 8}~ o 9 (9_&*0 8& 8 { }9 :p &{4o"9` :T &{9o { o l#0A[8& 8 {9(a? (8 {9(a: 8X (:88{9o { 9  38~ (o )8{9o { @ #(:j&{4o"(_(` 8J(  o* o = 8#{L(_j{9o { v(((s (i& 88{9o { : /8{4o"9 8{9o { : :|&#@[ +8c(  o* o {9(a?* 82~ o 9. 8{4o"9o 8{Ls. ( 88{L(_{9o { o   {9o {   ( {9o { (   (  ((   ( (s (i& (:!&{4o"9R 18{4o"s. ( &8{4o"(_{9o { o s (i& (9&(d~ P(((e $8u( J((b((c (@((:& 48/8 {4o"9} ,8(  o* o3 %88{9o { @ .(9&( 08{L(_{9o { o {9o {  |({9o { (  ( ((  (( (s (i& "(9&~ (o 68(h 8~ o 9' *8 (g 58 #?C (:r8m(f& :[& 8N{4o"s. ( 8(#@[ '8{Ls. ( 8s 8{L(_{9o { o {9o {  ({9o { (  ( ((  ((s (i& 8.{L(_(` 8{4o"(  o* o ( 9&{L(_j{9o { ((k(s (i& 8{L(  o* o ( !8n8~{L(_{9o { o {9o {  ({9o { (  ( ((  (( (s (i& -:&{9o { o l#0A[ 88{9o { @~ (:T8O{L(_j{9o { ((j(s (i&8#  E74.Ahar4C{ >Lb t q?>5.R* 2(:&#?C& 8*0 !8& <(:8{ 9 N8{  ; 08{4o$9 8~ (o :&{?o 38{ ;S D8{?o _8p8y~ o 9 P:Q&8Y~ o 9$ %82{ : "8{ 98N < Eg  u? mPR2G v"Vipj[TIPr)-Vs;(!l  n v& .> J L#\3;B:`  8U{?o 9>&~ o 9 8${ 9Z @(9 &{?o .8{ ; ]8{8(  o* o (m e8{ 9 K8{ o j[}> 8z( F9g&( '8T~ (|>( (o :%&~ R(o 8:&{<o 9&(d((& 8{?o L8{<o (:& M:&{ @ 8{4o$(" 48i{ 9 b8T8~ o 9 86{ ; C8 { ; 68 (n((& :(:&8{ @ A8~ o 9 -8~ o 9j W8{ 9 *8{ ; B9p&~ f((  o* o ( (o 98-~ (o >8~ &(o V8~ o 9C 8~ o 9- G8~ o 9d `8~ P(o 8~ *(o 58j~ (o 8L{<o 87{?o 8!{?o J8 ~ o 9 8( I(9&~ 6(|>( (o Z8{ ;d &:&~ H(o [8w{ 9{ +8b8k{ ; R8G{?o !81~ (o (8~ 0(o f8{<o #8{?(l 8{8(  o* o (m 8{<o /(9&{<o :n&~ o 9 Y8T{?o T:=&{9(  o* o o :&~ (o 78~ o 9 (:&( ((& )8~ (o 9&{ ; d8{ @ 8i{  ; $(:M&~ (o 19-&{?o O88~ o 9h ,:&{ @ 88~ o 9 S88~ o 9G 8{?o 8& U:&{?o \8o(  ((& 29T&{! 9V ?8>~ o 9 a8%( (:&{4o$(_(` (:&(  ((& ^(:8}C c8{8(  o* o (m E8{  ; H(:m8h{ ; 8R~ o 9W ;89~ (o (9&{?o X8{  ;$ (:8{! 9 Q8{?o 8*0"8&(9x& *8(o:) 8  ( 4(9&8{ o" o# ($ (:&8}{ ;h 88^X 8{ ;< .:x&{  ; 8`{! :$ 8K{  ;s !(:/&{ @ 38{ ; ,8{4o$(_(% (((i& 1(:&{& (:88f{  ; (9&{4o$(_ v(s (i& :W&{  ;~ 8?( ~ o  58"9 8{4o$(" %8 o o  884o" o#  &8{<o 8Q& '8(o? 88H{4o$(_ (s (i& 8Z{ ; 9C&9  (:-&{<o 288e o  9& o 9(:(& 8k8){< o o &8& (:>& (,: 9&&{< o o &8  EtR 8 9 (-~ o 9 88~ o 9v 8~ N(o $8{ ; 8 ( 78{ (9u (:8(  o* {4o$( o3 8]{ ; )8G ( "82(' & 8{ @V8B * E:HR%2=m$no%Nc54] ]iQRQ (:&# 8(o? 6:&X (:&~ (o 98s  8{4o$9A (:t&( ( o2 8V{ o  :<&{ ;& (: 8{ @j 08{ ;. #88 8{4o$( -8r?p 9&lD ((:8 +8{4o$( 8j*$0; #8& ?8{( : ;8{5{) o* o+ 8s, .8(pu F:&{?o : 8(- {5o. s/ o0 ':s&{5{Bo1 8V8{?o : 886{?o :  8(r( %8{5{Bo1 -8{5o2 68{5(qo3 ((:8{4 :( 38~ (o 9z&{4 :3 8d(r( 8N{?o :$ E83{?o 9 ,8}B 28{( 9 :(:88} +8(qd;2 :&~ P(o J8{5(qo2 <88C{5 : 5(:a8\s, (:& "8@8{4 9 !8&(6 ( /88{5 : (:&{?o 9 D8(qd; 8{?o 9 88,{?o :> (:~&{4 :g )8h~ o 9 A8O(7 {5o. s/ o0 8(8{) o8 @8 ? EK4rRaOLp ys(%k5UV]?m47!]!,g6R| H8{?o : 8(9 J({@({5o. s/ o0 >8n{?o 9 G8S{) o: @ *88~ o 9? :&(; {5o. s/ o0 48}B $:&{5 : #8} 8(< ( =8} 0(:&(= ( 8z8={> @1 9^&{5(qo2 B(:=&{5(qo2 8!{) o8 (? {5o. s/ o0 @8(@ D({) oA ({5o. s/ o0 8{5o3 88[(B ( 8}{? o 9 :`&}B 8N{4 : 898{) o: @ 8{) o8 @ 8{> @} 88q{?o :O C8}B 8{5(qo2 19&{( : 9&{5(qo3 8& 8b8%{4 9 8H{( 9 &92&{5 : I8{) o: (C {5o. s/ o0 98*0[8&((9!& 9&~ o 9p8{& 9&~ (o 8{5o 8(D & 8v8( 9_&( 8N~ (o 9/&( +:o* o- 98  E m U}C-% 8~ o 9 :&*0f$8& 8{5 *(oE (9&~ o 9 8q{9(  o* o o { (s (F &(9R& 8){G (t(ut 8&  E'8 & 8{G (H & 8& {5{9(  o* o o { o N({9(  o* o+ o { ( X(( ^((voI 8  E M/Cf' S/{ 8~ o 9(9$& 8~ (o 8& 8~ t(o (:a8\*Pw0%8& 8u{5oJ 8& 8Y{5o. 8 {5o. {5oK 8&~ o 9~ 8 ~ o 9(9& 8{5 (oE :&(8( : 8{5sL oM (:&: 9q&~ . (o 9Q&~ (o 82 (8 ( :8  EoVO Jof=& 88(N : 8*0 &8& (:Z8U~ (o 87: 8'8{8(w9w8q  E#jM4 d 9&(8p& 8*( 8~ o 9 8r}C (9\&{8{ o ( 8:{9sO ( :&s 8~ o 9>((:;& 8~ b (o 8*0 '8& (:88{W(}(| 78a8:{g( 38F{j(x( :(:'8"# 8{f( .!((( 18{W(y(| :&# 48{j{C( 8~ o 9) 8~#@[ &:f&{X{W({j>{W({j8i(~ 8)~ l!(o =8 ~ o 9 (:88{f( !( !(( $!((( 28{f( 8{j( ( 8p# ((9& 8L{Y(P ( (( ((( )8{9:" .8{i{j({j({j(Y{i({i(YY[X{j({j({j(Y{i({i(YY[Xso ( ,8_{Q((;:' '8@{j( 8*8{?o 9 (:&{h(y(z 088{h( (:&{g{Q({Q({Q(Y{g({g(YY[X{Q({Q({Q(Y{g({g(YY[Xso ( -85{@s !8 #@[8  E>yv.:# Tdl<NPdD:\D#! W 8{>lCq 8{?o 9 88( j[l :&{f( ( !(( !((( 59g&{h( /8Q{@r?p( 9 82{h(f l[Zj 98{g( *8{h( :&#?C 8#?C? 8s# 8_{d{Q((;(" "87#@[ $8{Y( <8 8 {h(f T!(#Z  Z!(( b!((( 8{9(  o* o o { o {<o Y{;o Y j[l 8\{?o : 68A8# 8(8N{?o 9= (:8~ (o 88p{9(  o* o o { :s8& +8{;o {<o X j[l #(:p8k#@[ 8U#?Cb ;8;{?o : 8 #A, %8*0%8& 8K~ o 94 (:-8({M(Q ( (: &oR ( 8~ o 9 :&~ !(o (:+& 8{S !(((8`& 8(s9 8l{j( ( 8R{U "((( 8-{T !(((8  EK*al5%eO  (:&{e(( 8{R !((( 8h( 9W&~ <"(o 88*08& 8p~ "(oS 8R~ oT 9E 89{^(# 9"&{i(8N  E\O\qL6m(9& 9&~ \"(oS 8{M(" 8s:N 8c{i( 8N{N(" 97&{i( 9 &~ oT 9(8& 9&{E(" 8*01(8& (:8( :o* ( :o* (o oU 8c{Q((?5 8C(  o* oU 8(  o* ~ oU 8(  o* oU (9&(  o* {7o oU 8(  o* oU 8y(  o* ~ oU (9& (9C&(  o* ~ oU 88 o* X ( ( (:& 8(  o* oU 8(  o* oU (:&(  o* oU (:a&( {Q(((oU 8/(  o* oU 8A  E`/&Lr, :rS*{M 8( ~V (W 8iX 8& 8U~ o 9 8<(  o* oU 9&~ "(o 8(  o* oU 8( +(W 8(  o* oU 8*0G)8& (:^8Y~ "(o (:6& #((s (F &86  E ~QQ8((9}& 8~ #(o 8~ o 9P 8{V({G (t(&~ o 9"8& 8@ 8.*!w0*8& 8O~ <#(o 81(: (:8~ #(o 8~ ^#(o 8~ #(o (:8 :&(s (F & 9u&8<(s (F & (:F8A~ o 9E (:#&r?p(988  E,PznH^^x}Sjl+ (9&{V(( 8s{V(((:'& 8L~ o 9 83 8'r?p(9 8 {G (t(ut ((9F& 9 &{G (H &8"  E8 & 8#&{V((:8 ( (9X&{V((Y*~ o 98Z& 8%~ o 9 (:8():] 8{V((Y*{V((: (:8{V((* *S|#w0)8& 8^ (:G&(s (F & 8"r?p(98  E (ij Q 8~ #(o 8~ o 92((:}& 8v~ o 9 8]~ #(o 9>&(): 8(8(s (F &8& 8(: 8{X *0+8& 8(Y 8^  EQ:kZ9 j :& @$((Z H$(( 9&( :$(( 8a( 8P N$((Z 84([  : &( (9)& (:8~ o 98F& 9&~ $(o (:&(\  9&~ V$(o 8( (:k&~ o 9< 8Q $((*08& :5&{=o] 8k& 8(^ 8  E ccL+ x 89 8}= 8{D9(9n& 8{=9! :q&{D(- 8[*0,8& 83{_s. ( 9&{g( t8{Z( j:&{G %(( o8{f  s8 ( =8{_ 0)(( 8{Q( '8m{I((| :8S{Ms. ( :1&{H({X( g8{d &(( a8{g %(( >8{H($ (:&s7 }G (:8{bs. ( 8u{H( 8_{W( :I&{H({I( 8({N D+(( H(9&{N b+(( T:&{L `*(( 8s< }f (:&{b )(( @8{U '(( [8g{\s. ( 8F{Y((| 8,{Ns. ( 8 {H({G( 38{Q '(( S8{h x%(( (:&{i( r8{Q{Z( b8vs< }Y #9`&s7 }F :I&{Ds_ }Z 8-s` }D E8{g %(( *8 (s9 8( 8{Gs. ( P8sp }J (9&{H({h(((92& 8b{e %(( s8B{E( 8,s7 }E 8{J *(( h(:&sa }T 7(:&{h %(( `8s7 }d 8{S '(( 8{g({i( ]8b{i((s8 ( 8D{Z ((( X8${Es. ( 8{Q( 8 J,(( 8s< }h 8{Qsb ( 8sp }H 8{H L%(( 8g{F( 9P&sc }c 8:{H({L( 8{g ((| Y8{^ )(( )8{E $(( G8{X &(( 8{Y &(( 8s{E $(( ~8T{e &(( w85sa }R 8 {K({M( +8( 8{X((| 9&{ds. ( 99&sc }\ 9&{I((z (:z&{j((| q8_{H({f( (9:&{\ ((( 9&{K( V8{H({g( (: &({H( B8 {J((| 8 {W( : &{N( J8y {f @&(( 8Z {Q($ 08D {g({j( 8$ {[ l((( z8 {H({d( 8 {Dsd }V 8 {Q( 9 &{g( 8 {` p)(( (:w &{a <((( 9U &{[s. ( (9. &se }Q Q8 {M( 8 sc }_ 28 {i((| 8 {Q{V( 8 sc }a 8 {Qsf ( p8 sa }S -8m {^( {_ {` ( 89 s. ( &8 {G( .: &($ c8 {K( ;8 {Qs. ( 8 {Qsg ( W: &({J( M8 sh }i 8k s: }P A8V {c *((8\& K80 {K *(( _(: 8 {`s. ( D8 {Z( d8 {H( u(: &{j +(( (: &sp }g %9z &{M +(( 8Y s. ( 8= {Y &(( 8 {f((| $8 {a T((( 8{I x*(( 8{H({Q( 8{K *(( f8{d( 8o{i +(( <8O{J *(( 9/&{T '(( 8{X( 9&sh }W 8{H b%(( 8{X( 8{I *(( F8{i( 8z{O +(( 9Z&{L( 8C{L( 8-{Fs. ( N: &{Zsi ( v(9&{i ,(( U(:8{V 0*(( !(9&{H({Y( 8tsp }K O8_{F $(( 8@{H({e( :&({K( 8{] ((( 8{Q p'(( y8{W '(( 68sc }b I(:8{L H*(( 8e{H({E( 8E{as. ( ,9#&{i( 8 s< }e l8sc }[ :&{g( 58sc }` e8{i((s8 ( 8sa }U 8{V(( 8hs7 }M 8S{] ((( C92&{b )(( 8{X &(( "8{\ ((( 8{X( 8{Qsg ( (:&{j +(( 8u{_ P)(( ^(:P&sc }] (95&sh }X 8{j((z (:&{H({W( {(:&{h((| 8( (8{Y  s8 ( (:8{Z ((( 88e{F $(( 8E{g Ps8 ( Z:#&{h  s8 ( 8{[ ((( 8{c )(( }8s< }j x8{` )(( 8{W((| (:o&{f &(( \8Ns> }L 89 X,(( 8{R '(( 8s7 }N |8% sj ( m:&{K({N( i(:&{W F'(( :&s. ( 8hs }O 8S{H( 8>{H({F( 8{W(8h  E%ED}{0:y,R?O0 z Ks&| [AY;Be}F|Nh }0SbJ+]ug b;L_gg`m ~)= ,Ra1}7+&,[9 ?o t: mR *f8&  o? *&8&*&8&*:8&([*F8& ( *J8& (R *F8& o@ *08&~A ~A sB jj~A sC *"8&*0108& |8    8  +8(.: rY 8  9Y 8y Y 9W   p ZX ;8-9/  IY 58    X 8 k X 8  8F-9  6Y 8 8 S TX 8k    :J 8,:)    8,: TY 8 K .X 8  68  8  X J8#,9v  i SX *9R    86 b IX `8!    !: X 49    9  *Y V8 {Y h8   tY 8}    8]   7 X A8B x 0X 8)    X ?: 1Y 8   >Y 9  u Y W8  8L* 8    $8,*:k GY 8*9H8D  EY 8): Z ]X [8  5X 8   9    8    4X 8   d GY 8q  E 3X [8J }82 8 ) <8)9    L8    U9   1Y `8    8  8(98 U X D9f JY 8C   NY 8(9 /Y 8  v: L $X \8':,"8   @Y 8   8    :n 8p'8u'8z'8' 8E 8t' 9/ V X 08   8  ,8   HY :8    8   LY ]8 { LX C:v  8b    @8B =Y 9$   1:    98  nX 8&9   8   I8%: 8  R8o8% !8Z8%j8% z8%988  :  9Y 88 NY 8 M pX 8   q8    w8   MY ]8  |X @8^ @Y 8I 0Y %8$:8  k 9Y 8 MY 8   8  8  8 s rX 8  8k    X Q:K , @X 9-   . zX 8   X X8 8j#8;& 8 :    8    8  k jX 8]  wX 8>  =Y M8   BY H8    8i8l" 8 GY '8 KY K86"98    V8x  ?Y 8U   o X 91  |X 8 5 Y a8!:8  28 9Y 8 L /X 8 k UX 08 Z X F9f D tX 8I   9, % X 8    8 cY #: )X 8    ;8 HY : d X 9f 1Y 8I  K81    &8! 8Y : SY S888rp88 8~8 d8    M8   CX 8n . ;X 8M   d81 + X x8   8 | Ee}o2^c^ 4 P8.Gv dd 5+; zGr  .oSN@ho 4*  vrFr_b.F+ r [|ELv  ~e]/ Wwx% > k suS G9]2uZK l}G " kf  BM T| XFC[!`I ;g[-VV7Dr: zp66 Y,"  N !&-tKR S ? _W gK Z gc5HIb+{J > y\) A -+   ??>< I8U8/ 8+:89    8    g8  :  V (X 38   (:  ,8   68  SX 8e  o !Y P84:^88   z cX 8   8 <Y 8 X )8 FY X9  CY -: 8Y \8x  78` X 8G -Y 8":! H 'X 8  1 -X .8  mX 8 DY 8  8   2 vX +8n X 8Q:F   a8*   X 8 +X 58 ;Y r89   9   .Y 8 2Y 8{88i8 8:N lY 89 KY _8  Y *8 X 8 8 5Y F: FY 8    :v    H8:U 98   b8$ W X 8 o pX 8 LY 8    Q8i>s 89   ~9~   |X :Z    :=    8)  8  8   hX f8$:68  GX 9 FY G: j =X t8e X s8L   { zX o85 BY Z8   8  GX S8    8    8: l Y A8:8  oX 8j O X 9L 2Y Y:.  sX n8 @Y .8 4 tX :   3 (X E8  89   G X e:v    %8Z  X 8E  =9$   X ^8 8 DY 8   % <X 8   B8    8    l8e  8 8O   g JY c88  5 kX :   8 7Y 8c:8   /Y u9 6Y >8  T8z .Y W898S    R8:8-    8   SY E9    b8 BY 8b9    88H 8D: =Y 8b @ X P8::    8& 1 [X 8   8~i:- 8 yY L9  c8 DY : 2Y :_    8K  9Y 8 :8    y8 98  O8 :j8    8    8 9    tX /8z8f 8j 2X {9H   O vX :$   8  @X "8  N GX p8    8 L /Y :  HY 88 4 +X 8^    8F   " yX (8'   j X Z8   HY D9 8  8 [ X 8 _X C8 9 T X i8 98g  J8K ? X 82   MY 8 SX :8  Y Y #8    8  j8 W PX :   28 98a  /8E  kX 8(  YX N8    8    8    :  : X Y8:v8 @Y O89r8n   FY >898A <Y 8,   >Y 48   U /Y 8   DY 8    T8 <8   : SX &8  @Y 8a U 3X k8H 7Y _8/   8  G8  OY 78    rX 8   38   GY 8:U8    8c9* 8R  kY =8:8% LY U8   QY 8 AY 8   o Y B8 :8 $X '8 Y 8y   8Y    8:87  b Y 9   + UX ?8    9   a uX $: SY 8    98z  8c    UX8:g& N8< <Y : ' bX 19 \X 88P "8 j iX 8  8   >Y 8z  HY 8[ -8K ?Y )8* < wX m8 UX 8    8^98~X 8888(38(28 &r?p**(48(58B(58(484(48K(58(/8(48(48(48sD 8(58(48t(58(58((!8ok 8((8|()8wsE 8(58(58(-8<(#8C($8@(48D(58T(.8(,8(48(58_(!8rok 8m("8msF 8h(18u(*8(58(58(48(48(58(#8n(&8i('8e(48k(48Z(58(58(48(58(58sG 8(58(48(58#(58(08(48(48(58(48 (58<(58p(%8(+8(58Y(48q(48m(58/(48L(48o(58'(58w(58(58Q(58@(/8(58*$.>b.0)18&rpoH &(I (J ioK *"8&* 08&(L &* 0.8&tRoM ( 9tRoM &tRo2 oN o* r pr?poO ( 9*tRo2 oN o* r pr?poO n&o rpoP oQ o* ( 9'o rpoP oQ o* &r?p**(+28]W0_28&sR  oS i 8oT X Y= 9oQ *?P 0k38&sE (U  %П( oV %Р( oW oX sG ioY oZ o[ *0 48&*0 48&*0;58&r-p o > * *0;58&rap o > * *0;58&rp o > * *0;58&rp o > * *0;58&rp o > * *0;58&r1p o > * *0;58&rep o > * *0;58&rp o > * *:8&( *J8& (R *V8& o4 *F8& o\ *V8& o] *J8& (L *F8& oS *V8& o^ *F8& o2 *F8& o_ *V8& o` *f8&  oa *v8&   oY *F8& ob *F8& o[ *F8& oZ *F8& oc *Z8& (d *:8&(J *v8&   oK *&8&*&8&*N8&(7(8*:8&([*F8& (e *N8&([( *&8&*&8&*0S8&(?ok (@(A(B ,((C ,((D9 (E8*068&(F(G   %С(H sf (F(G(IsE (J(K(L(MsG i(N(O(P(B*N8&(Q(R*J8& (R *F8& o2 *F8& o_ *J8& (g *Z8& (=*Z8& ( *:8&(<*:8&(J *V8& oh *Z8& ( *V8& oi *:8&(U *V8& oV *V8& oW *F8& oj *v8&   oY *F8& oZ *F8& o[ *:8&([*F8& ( *&8&*:8&(]*F8& ( *0&78&(b(c `sk (d*z8&~: s^&*0r88&{: sl }{(e(f(a(g:(hsf (i(e(f(a(j ,((k(l(m (nok (o 9m sF (pj(q (rԍi(s&(t(u{(v(f(a(w&&r?pr?p (e(f(a{(g9{(gtR & *(g>w'Bi0)8&(f ,(x ? (y *F8& ( *:8&(m *V8& on *F8& oo *F8& oH *V8& op *J8& (L *:8&(J *V8& oh *V8& oi *J8& (g *Z8& (s *J8& (R *V8& o4 *F8& o\ *V8& o] *F8& oS *v8&   oq *F8& oc *J8& (r *F8& os *f8&  ot *V8& ou *f8&  ov *J8&(_(*68&(|*:8&(_*BSJB v2.0.50727lF#~F|_#Stringst#GUlDu#USE#BlopF #GUIDfD#BlobW= $|,}*8  (FlqF5PkK#2KFc 3Cw|9Q1|u|| o   O ^ |  | | | | |4 |R | |# 4 @    "L Q o |  &s|$|||\{7||&F|%|H|V| |& &&,||&(| C**!7 Hn *S|x|||||"|* .$ / oM a "| Q " Q . / 2  $! e!u!"!Q F"|""*\*D#R#"i#"~#"#"$$$$$$$%%%7&Z& & & '"'*'x'|'|'|'|' ' 'CG(X(w(X((().=)/ )%)()* *$****+",+Q =+O+"+Q 0,%i,%.,/ *,&W-\- v- "-Q #.. . .8/|F/ P/|e/|}/|/ /|&'000w11|6v222|.2/  3$|3|3|4$Y5|7$J78$i99:|:|<.??@ .\/ @ACCD E|G|PH|ZH|H|H|0I|K KC&KK$!L|".LQ LrOQOOP,PkP]QjQQQQ QQQ$QQ R$RQ'RQNRWRRQS{S$S$S$S$SQSQTQUcVQgVQtWQRX{XXXX ^F^^^^ _ [&_H_Z_i_[]]]'a\]!^le#pi,m1ik q{]~ ]~0uf]q] ]yCV]i}6]9]<ySyW q[i[ ][ 3]^F]zqzqzqzqzqz]zakE h r           * H [ e o y            E h r      CVKVOVXV[QcQm_Qw_,=Xc_'2=Hbm4?JU`kvCVJ V K VK VK V&K V/K V5K VBK VOK V[K VdK VrK V~K VK VK Va_Vk_VM_N O O a       (() z*M- .s +    :-8*(     (   ( akTCaM WZ[[/[C[W[P f)!&H%"AX%7Wh%F[x%Pb%Zh%ml%q%q%u%u%&u&6 ,&S D& uX& p& &f)& h& l& q&2 q& 'a T'fT(aZ ( 30( ) - ) /  /  /  /  / /* 0kZ 0MZ 0WZ 1hZ 1r |2) \Gh lGq xGq Gu G' G: GM G` G H H 8H PH8 hHT Hs Hu H H  Hu H% IIl Ih% (Il @7:>lT7X>lh7r>|7>7>7>]7>u7>l7?7%?  8J? 8j?u08?H8?\8?E p8?E 8?8@ 82@ 8U@l8i@8@ 8@ 9@u 9@49 A D9,AX9OAX l9nAu9AW9A9A 9AW9B]9B9(B]:G$>G<>GT>Gl>H>rH>H>H>H>Hu ?I $?JI]Xl(f(a(QQ#*Q6,f-a -QQC-QM-YXh-f)-gXl-f)-Xh-a^-k-Xl-X -Y-,+Yu-@?Yu-TSY-lgY -{Y -Y-Y-Yu-ԈY-Y%-Y-Zu-,Z-D/ZC -XCZ-xWZl-kZu-Zu-Z.-ЉZ-Z-h-Oh-(k[h-1111  11z1   z?"["-- z1OAQHQOQOQxQAQQ     f)!f31fAAf3If3Qf3Yf3af3if3qfyf3f3f3ff3f)f!CGN!Sf) z ~)  1I W1f 9~)A , 'Q < ' )fT S b f 3) }2 ~; B g f  Z ~  9) arif)fff)f)f)f)yf)qf)f)y  f  I WWyAy,K3gS~3)~81r)%)i853ABq;IAqByPYLWoetAyg>)f*B ff)f f))e3 h5WrW&)FAy5-PD fff f iF )qff*u 3y8 f)f ?$f)f),f Xf)f)f!d4f=!tQ!d a!Sa!3!)a!S!C)2 ~a!3y!~y!3!!_ "a"S%"92 ~]"ym")")1""""""f)"3 #)#_%#36#f#$##)##$2  -$C;$ f3R$a$o$f3<f $$C$ $WD+ /%1!B%~X%#g%3v%A%8Df)%D%"%Kf)%3"3f)&3%Kg%3v%AX%&A"3"#%<&~9d&&&"{!f)A& &)IfI&~ & & '! L8'!F'~!V'3Tl'!'~ &y'_'3(3 (~f)(3"(30(3 <(|9(Cdf )Y")1)L)[)_lfh)v)C))))~tf *_*(*f)$+B*O*f*|f *W*Z2 ~*b+le!=+w++f)++ f+++A+A+u!+,,?,P,},,,,9,,-~-9$-AD-g- Q}- I- Q- $ -A -) f$ H A-O Yf] ..@.I.~aj.3ap.Si.q._y. I._/W92 /W%/Wr)rIf f)1f)fQf)9f )f)ffAf)ff/ / A0 )A0 0 0~ 1~) ) )k1 f  11AI W1 12W22, i!4 )< 3 03C[32V )3d 334C74DI W)4i )4~)4, 4d 2o )#5d5A5!553 6v ,6~a66)6363!7{ )  !u7~) 7A1 ,8;8 I Wh8z8z14d 8S8~8Si9W)<9))U9 9I W)9 Af *: YL:WY#5 q: I: )I WI:q ; *; 9f A8' l'zY ;;;u7a=< )g< <)f <)8' q) ->)N>))l>)1> > I W e l>)<I W9?^?~?? 53!S?E ?E J '@F@$l>)i}@A@I W@z@WAHAAcA] AAziASAYAW2WYPBW1ABWBWBWBW Ck B 1K3PCnC)Cx 9C  DS DI WUD DD D)YE E FYBFWAdFFWAE F F )F)#G )JG)G )G G G  H/H   F H 3q I 9;I 9^IeIWYIWBJ zJJJ) f K K KfKL3" \LhL L L )u7~[M M)M N f)1O O$ I P3 Q7PWI]P: PI 9PU .Q[ Q f)f)f)f)f f )8R~A=R cR B R R~)R~)R{R SfE4CSSSiSoSoSuS{M)S2TTTT)TTSS!QS#R%cS'oS``(`H`X`x```~C|M Q /  2[[[<[?\3)\7mP\;w\?\Co\GE \K ]O :]Sw a]W ]{ ] ]R]{]/^=sc^  !"#,IVCIFreeDiscBurnerBooleanSystemmscorlibRuntimeCompatibilityAttributeSystem.Runtime.CompilerServices.ctorVoidAssemblyConfigurationAttributeSystem.ReflectionStringCompilationRelaxationsAttributeInt32AssemblyTitleAttributeAssemblyDescriptionAttributeAssemblyCompanyAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyTrademarkAttributeComVisibleAttributeSystem.Runtime.InteropServicesGuidAttributeAssemblyFileVersionAttributeNeutralResourcesLanguageAttributeSystem.ResourcesDebuggableAttributeSystem.DiagnosticsDebuggingModesAssemblyKeyNameAttributeSuppressIldasmAttributeAssemblyDelaySignAttribute8ab3e03f-a509-40c0-9748-b1698fff3facFreeDiscBurner.exeYRNhRr4j0FsknLt8rAvQqh27CJiMMYP8w5r9ObjectListViewColumnSorterJe86RK63fBXcmwNbQY3LXCLJYmdP5uWYqeXEOptionsFormOptionsFormBaseDVDVideoSoft.DialogFormsStringResourcesFormDialogFormSystem.Windows.FormsMyDialogResultsEnumMainDvsMainDVDVideoSoft.AppFxPNqRWwVLB4923jmRX2fvValueType0klGdRlII0sVI4oEckxNEV7H1rDuOHOBXbDJResourcesFreeDiscBurner.PropertiesSettingsApplicationSettingsBaseSystem.Configuration{CB9AA69F-4951-49D2-98A1-18984DCFDB91}__StaticArrayInitTypeSize=20{9EF7AF60-4C56-4FC7-8D08-014E7C2D5F69}r0FRMCtP7ewaoFygJ7jnCU9aFHZh5wmf6bI2SFU4mbT3GMret7THonfMulticastDelegateYvktN9NN8wd4pmsbLQG84xgbu3UR2KgWPRUYiLGkmDLVFCGQJCe6WdAttributev1YGZ6ZXPZ7Gm9rymS`1s1k5XWVUBkaO14OfgSFVS5B4xjiXrO6pJHQ8Ers8VfR0d6iPR1Wl9D0Cs3EeciDxRacdXaXJAFj5lmPAsiLphbQJ9V3dxLlsMageREUkUVgyq3JBw1Sj3pY14lMegSBHXd3FHml9sKs0D8D6P7GTHrTTHwTnE7eNq8{7F08E812-B9AC-421D-B143-19E5206A834E}__StaticArrayInitTypeSize=32__StaticArrayInitTypeSize=16__StaticArrayInitTypeSize=64__StaticArrayInitTypeSize=18__StaticArrayInitTypeSize=15{CED6773F-B4E3-429F-B0A7-214930634C6D}X7OogKkbGTZd4FFQMPSortOrderComparexyListViewItemDoubleDateTimeIMediaItemDVDVideoSoft.DiscBurnerApi.MediaItemDVDVideoSoft.DiscBurnerApiget_SizeOnDiscInt64CompareToget_IsDirectoryset_SortColumnvalueget_SortColumnset_Orderget_OrderqX1OBWlMIpeYV4QdGN62UKcR0yx2PCvqAuvXL4IwfnZmlC9OiFuYUJR9IVNRkArErpiRxV6QerCYFQIr4n3qmOZVLpget_TagxpyJCrpSudRFvlBi89get_TextIp8E1mMKvWwcUCdVhfop_EqualityKuXkoLGwF9AWjZwVFsget_SubItemsListViewSubItemCollection2Z24t4xaZ1jIOaWAXUget_Count9l1SXMfGWyOq6Cjtutget_ItemListViewSubItemRH5GvRPWOUBqNjSMr7Iy5aTehgyRVUCqSZXLXEZCCwBNo5eoewOpWNConvertToDateTimeSortColumnOrderIComparerSystem.Collectionsn4jowRXs3NTnhKl4Pkym9Jofck0P4s6o1VO4360PDYyIUbemLZ0C8LoTEvMUepEq7ejWH5S5Pwnl6Kb6REventHandler`1EventArgs0qK14WRxbNnQyImYFnIContainerSystem.ComponentModel2u2DYlnsXButton8VUNYJEBLtppu4noGFTabPage1wmvf8Wgdyg7eHaMVCComboBoxmshapGUpILabelMDfAGRNcsHqiVY794MTabControlBQTgr16MEMJjF355Lh6JW6TH5iaFolderBrowserDialogX4XThrMZ6CheckBoxVuVi1iLFYAUJ30i4vJqknjX8bBGLLQphKNbPVHqcfh6D6TextBoxupWIBCNyo8DKSZgQFZ7xyfoBVjjQjAUIqof5F61Q9XYPDeqQhNbNSToltGocgXWqlCxUssB1IQFrCtRNHR7fdRHVESadd_DriveOptionsComboSelectedIndexChangedDelegateCombineInterlockedSystem.ThreadingCompareExchangeremove_DriveOptionsComboSelectedIndexChangedRemoveparentInitget_CenterToParentFormWindowUtilsDVDVideoSoft.UtilsCenterToParentSetCulturecultureDoSetControlValuesComboBoxItem`1PropsProviderproviderToStringGetIntGetStringDefsDVDVideoSoft.AppFxApiGetBoolEventHandlerIntPtrget_ValuelanguageCombo_SelectedIndexChangedRememberControlValuesGetCultureFromComboSetGetLanguageComboGetThemeComboget_DriveComboset_DriveComboget_SpeedComboset_SpeedComboOnCheckUpdatesClickwUHCsRWOigHu8Xi095InvokeTUYYjgtvVDisposedisposingpyoHM6Vc6ComponentResourceManagerSizeSystem.DrawingTyperJWKbXYjgZsoerlhLY5NoGtag1WqSYO8vfAtoHtvekUKNLLoGQJGHAasv2x3VdOYDQY4u29Nget_ItemsObjectCollectionI4CCo2aUPvlnDeaqvOOIQs8pwkHXC4ebNEl24KoeoOjqeJ4vnCK1fZXPeMByuJFqLNIW7WPgListControlget_SelectedIndexNXGAN55eW4lQir0844remove_SelectedIndexChanged7WGado26eyKk2qqHCHset_SelectedIndexuN9HJyANExIqI5fyssadd_SelectedIndexChangedN2tPsbL19pP3B3EwYYControlset_EnabledHMYE3nbBaRIhLd3eKhset_TextZebax03LcXWu4hfwd2set_CheckedQjUmpdQNaH4NXA1u8bget_CheckeduBkcIB7J52kAfC52TWrNCTmJiEFUyrA3gMf1set_SelectedPathmm529bJDqc8pMARGfEDialogResultCommonDialogShowDialogb5QtAfNm5YlYtZJ1M1get_SelectedPathQbiihgDNnSUHMc5VN6set_DialogResultNNQdkOn1yIhkrTBfXFIDisposableJ02UIfSBQbgsyLaG9tRuntimeTypeHandleGetTypeFromHandleBWof3SOS9M2h9uFmdNSuspendLayoutSrIiEKrXEdnkQxnLlwIRIHlXd2Ofbuf0ecDprxGFHbWuqyD2pYvFwbApplyResourcesilLqsEKjeFkc0iFinjset_NametNPFFp6v1L6m6OR2k1ButtonBaseset_UseVisualStyleBackColorliW6S79WcCUhuY0YVtadd_ClickUMOFBw1Z0y8bjgAok0get_ControlsControlCollectionadL0iSvoKr43eOVSJiAdde0TwdSspHDorZuhETsLYFKLBEqBDlFKAJ74qComboBoxStyleset_DropDownStyleq5QcorFShuKMckuPKLset_FormattingEnabledThcIN5Hd5hRAOYreGnColorget_TransparentvIrRXEq4rlrFF2V7EFset_BackColorbdIjwxTeAsl9gwDEgpCheckStateset_CheckStateAN1CfT8VVOZjPcTs6gset_MaximumSizexKrXcmtxhreM4aXhN4set_HotTrackOx6PdjzgBkSXjeJOc7CJVjJ4omW3CBpms5pPaset_AcceptButtonIButtonControlgDD1rIooueCEm2A5pmHAutoScaleModeContainerControlset_AutoScaleModem4TkyLo4LpFeOogEdNkset_CancelButtonPRJnFdoC7sSckqnDI9xYbhmowoRIcOYElkbcuauwHAQwoZ8heMRHmQLscset_ShowInTaskbarkRsgPBokojZ6VoKBN2Kadd_Loadg0YdiwolnhQ1ilS7yFmResumeLayoutMn6eZto0dxUYEbGxgjUPerformLayoutLwu9EuoItRUM7WjG2g5DriveOptionsComboSelectedIndexChangedDriveComboSpeedComboresourceManResourceManagerresourceCultureCultureInfoSystem.Globalizationget_ResourceManagerget_AssemblyAssemblyget_Cultureset_Cultureget_AddingFolderget_CannotAddThisFolderget_EraseBeforeBurningget_EraseBeforeBurninIsoget_ExceedingSizeget_FileCanNotBeDeletedget_FileCanNotBeRenamedOIw2L7oGZpiRe0JwMgvTiWmjvoxIZ7B64vbhM2kY3It7opjW4TTU5WAkyE8X6ZHoM8tci5yyUu55n10OXpof7v7a5FbPEYKReferenceEqualsZMUjyLoPTJpfQVMJWbFrHyLlwohx4vhnJmrUN1CultureAddingFolderCannotAddThisFolderEraseBeforeBurningEraseBeforeBurninIsoExceedingSizeFileCanNotBeDeletedFileCanNotBeRenamedPanelfilenameAddProcessStringstrcolorShowMyDialogPointSizeFSinglePRXE54oelyfijNIXPSsIwrqhgoXPcb4l9jBBnWtnXiY9oB2LOAcnu0Tu3pI7kwMoypMT8rMHYaOh0Hm2eBoc0NYJpaPJoXSConcatgyF7cRoge22HhvdOpsdset_ForeColorQQrID7oUMSjN3fjDkVcTextBoxBaseAppendTextDhUX2DoYjHtaJowHjgWApplicationDoEventsg2tAsfoVHL5sgfyuspISystemColorsget_WindowTextIjUOVsoacrn0ZuwYIKAHsUX9Wow0AfP3Udau3FyT6Z8lojwX6RXTnBWDARDADbgoucnatjj4HIg2HGWoKDo5gxFU3KWajWLA0CJqko2niqRgkjaMV7get_ControlDarkKatGRCoAflVbpDuGjI4UpCL1poLcgNXdupk5GEset_LocationLO1yejobcGh1Y10AgIIKxw8Hwo3EBbwRT87esVset_SizeNsJ6dToQc61ZbCs5PhYset_TabIndexhRcmoSo7TQrtM1WSjX5WPlDLwoiZX1Vq61ZaLR5iL2XroJYRiTPOcU4bDluxvDEoN4tVO09svggEG7eeLWoDxX7Hb634R2A1jDLnkonncx6EuYc1FNset_Multilines8t9R4oSUeucq0Ye91nset_AutoScaleDimensionsKj3UwgoOWxTg10rEXRcXXueQhorqT544ByCw9Pset_ClientSizelEcYALodNrTe9nd8NWbset_ControlBoxAtAtX3oWJ6r4RIry5INFvUJP6oKXHJnkIaPGhjFormBorderStyleset_FormBorderStyleT78nnbo6opTrR2fEThmset_MaximizeBoxCex1E1o9WiMgGBZDsXAset_MinimizeBoxM2TywRo1BfffDSbJD1UZsRhwwovoEhNAPWUjXaon5BO6osnrKdEGDpnQsd2pgE0oE5l9ZZUXCbxHvalue__YesYesToAllNoNoToAlltFXEdQtmWG2L2f8iHkgogKgkAoJ9gywSkQvi6Tyzf1mnEFormProgress4R5omsWoTkYdQoonq5Q8IBurnerManagerjKRo4IS17PBurnWorkerDVDVideoSoft.BurnUtilsB6GoC03RbvList`1System.Collections.GenericBurnerInfoI2Uo876IruStackW8loYSqLyYMediaFileLayouthfHoHs7UHUcfool8A8jxDeviceVolumeMonitorPVJo1mqho5AYuoyI7iMlBurnActionh9poD2uXbcFLFoN76uad9iSou98Y7ygsZovtHpjacYHoehUCgBQUYoalMgOvUfBoABrEqtnU4oVwo6aM8wLogEPiidAafoFWfPmyMPqo60XAkrXdSoTQagGgbFjoiVveLJeM9o3BpvZqFJCojVTAKuBOOopuNHi3OpenFileDialogOZ4ocuWH4X034oIl8goGListView8xfoScDXeEColumnHeaderUfLofrCsU0sbtoU1t6CcPq1oQptD4VsQNohIadMWImageListyUOoGeSeQ8PictureBoxlfGox4L4bDiXnorm9vePp9qoddvDhNContextMenuStripDBeo571Xo6ToolStripMenuItemYEwonMJxbi8UNoW0C0I06aloPKPjRCthVo9GN0y2FsDoqMuGUqBlnoB9kcuhkWFoJixkXu2LmoZPsDVqycVos8ObJNI3WokuRub4tKwobna1vulKOoRrO8lDH5EoOcHHY224LoMi7kCGeqwoL5k7QJadd_CheckUpdatesClickedBurnEventArgsadd_DeviceListChangedILogWriterLogDiscWorkerDVDVideoSoft.DiscUtilsCancelStopProgressHandlerLocalizationManagerlocMgrILocalizableVoidStringDelegateadd_CultureChangedTaskCompleteEventArgsadd_CompletedBurnManagerDVDVideoSoft.ImapiDiscBurnerDeviceVolumeActionadd_OnVolumeInsertedProgressChangedEventArgsadd_ProgressChangedadd_OnVolumeRemovedBurnerExceptionExceptionget_IsInfoEnabledInfoStartget_IsErrorEnabledget_SystemErrorErrorget_CurrentCultureApplyCultureForAllIPropManpropmanApp_Title_PNset_Itemget_IsTraceEnabledGetProviderOnFormClosingsenderFormClosingEventArgseshowOfferScreenWriteCurrentCultureStopGetShowModalInputParamsToEraseDVDVideoSoft.DiscBurnerApi.InputParamsset_EjectMediaset_RepairUnrecognizedRwMediaset_ClientNameget_PleaseInsertDiscEmptyShowProgressFormset_FullEraseBurnerTaskDefInputParamsToBurnFilesInputParamsToBurnIsoInputParamsToBurnTrackAtOnceInputParamsToBurnDiscAtOnceget_DiscIsNotEraseableContainsHideAndClearset_ExclusiveAccessEnableBtnCloseDriveInfoSystem.IOget_RequiredSpaceGetSizeOnDiscget_NotEnoughFreeSpaceget_ClearPlaceget_FreeSpaceget_NotSpaceOnDiscFileInfoDiscSizeInfoDVDVideoSoft.DiscBurnerApi.DiscPropertiesDiscSizeget_TotalSizeget_InsertDVDDiscGetRootItemCountBurnFileSystemTypeDVDVideoSoft.DiscBurnerApi.DefsGetRootItemByIndexget_NameDefinedByUserset_CloseMediaset_VolumeNameset_DiscWriteSpeedset_FileSystemTypesset_MediaItemsset_ContinueMultiSessionset_VerificationLevelBurnVerificationTypeset_TempDirectoryForStashFilesset_FilePathset_MaxWaitToFinishSecondsCharGetTempDirectoryForStashFilesFileUtilsCreateDirectoryWithAlternateget_DiscWillBeCleanedget_DiscContainsDataFunctionsConvertBytesToStringWithSpaceDelAllMyFolderItemget_dataAddFileSearchAnyItemAddDirectoryIEnumerator`1IList`1IEnumerable`1GetEnumeratorget_DisplayNameset_NameDefinedByUserget_CurrentColumnClickEventArgsget_PathDragEventArgsDataFormatsFileDropKeyPressEventArgsCancelEventArgsIEnumeratorFormPresetNameset_BtnOkset_EdNameget_EdNameset_LblNameset_BtnCancelset_CaptionDelAnyItemStringCollectionSystem.Collections.SpecializedStringEnumeratorGetOutputFolderPersonaldgE5aw5DDcfFn6aBKNITagProviderDVDVideoSoft.DiscBurnerApi.UtilsSmaWm8JAYOR1Px3LnhF0b9H96WkCheckForUpdatesyysq1JPNCProcessBurnerInfoListIsDiscValidDiscResolutiondiscResolutionDeviceLetterIsBurnerValidget_FreeSizeBurnMediaPhysicalTypeInsertedDiscRecorderTypeCommonUtilsMediaPhysicalTypeToStringIDeviceInfoDeviceInfoget_FriendlyNameProgramsCompanyRootUrlget_NoRecordersget_ProbablyBads6sBGnGxDCanWriteDiscget_DiscIsNotWriteableIsInsertedDiscBlankaneJil6ktDiscWriteSpeedInfoget_WriteSpeedAllDiscSpecDVDVideoSoft.DiscBurnerApi.DiscSpecGetDriveRecordingSpeedUInt32MediaWriteSpeedsTryParse78XZA2mflBurnerTaskEventArgsProcessingStringBurningFilesStateDVDVideoSoft.DiscBurnerApi.BurningStateget_ApproximatePercentSetProgressget_BurningTracksGetColorProgressColorKindAddProcessStringsSetTotalProgressCancelledget_ErasingErrorget_BurningDVDFolderget_CurrentActionTypeBurnActionTypeget_ImageBurningget_CurrentSubActionTypeBurnWriteActionTypeget_ErasingInProgressget_BurningErrorget_BurningSuccessActionTypeTaskKindMessagesTraslationsBurnActionTypeMTget_BurningFilesget_CurrentFileget_ErasingSuccessBurnWriteActionTypeMTY5asir4WTShutdownWqCkB0lMIIconSHFILEINFOSetCaptionBaseWinApiSHGetFileInfohIconDestroyIconCompleteProgressFormsuccessmessageset_DisplayModeProgressDisplayModeRemoveLastProcessStringPlayFinishSoundFinishSoundN36b8XNiH9khRSi6HMget_OccupiedSpaceget_BurnGetAppTitleWithVersiondLeON2LoiDebugget_IsDebugEnabled5MWMQCsbeIPropsSaveToHelperAddStorableValueLocaleUtilsDefaultLocaleNameExtensionMethodsKOdLRq8NboqM745Bq43AWXAbKRXszTypeNamerls0AD6Zfget_Monthget_Dayget_YearM5ptsYnT4ToolStripItemContainerKeyPressEventHandlerColumnClickEventHandlerDragEventHandlerCancelEventHandlerFormClosingEventHandler1ebjBxo88kPuZ3AThj4WjcONDotf6Wx4tJKDjeset_IconNatnNZozhvkysdyUMKDget_HandleVxeBCs4m94Rhg3A2vTXset_ImageImageoI7lep4oh5qk2cfr19wset_ListViewItemSorterr9SI1Xoq2u2grkVitxPBQa2dNoTACcLVCgTg5JyQLO5b44TiHNGZ10visEnvironmentget_OSVersionOperatingSystemSuR38y4Cox0XojZAmCXget_VersionStringdIoo9l4RjmDefsMWYDWCjeQaZ4ZojpqdiAwvl6yZYr4J4kimDOnfoZYyBget_StackTraceGt2R3s4lGdvj2i8DGqtE4b6tw40nlS2igNyd61aJsjRA4IPnYNAKnde74get_ColumnsColumnHeaderCollectionaLNsKi4p5Z4u35BDqMn6O5V3V4MVo22uuZp6yPset_Width7ocnmv4GKl64em5ODkEXBngie4xxNBx9DPgOaIFormWindowStateget_WindowStateJG0koS4fW8nW4LuNumeget_WidthoOUbYQ4PciPIXap0JSlMuFsoi4hTDAI4ubtJnGRmQ8i64BT1muyRVOrCd9jSwMg4ysq322tUChsvCommonDataDVDVideoSoft.Resourcesget_ErrorptMF7q4ernfZTgqnGLoMessageBoxShowoDKnVX4X0R9iWR16bfXITaskDeffoueBw4cnfkVnpayZjh7eeIAd4g0BbeUSX3JQRPathGetPathRootc7c5De4Ui02d1uQWJXHget_AvailableFreeSpaceFqr92R4Y72pO8v50Tp2get_ContinueMTqh2T4V4rAyHPigC6RMessageBoxButtonsMessageBoxIconIuBpaE4ajMgy9fDSkg3IsNullOrEmpty2l8Vmj4w3jeQ5uiaXEIget_OoopsNoFiles4crFQr4jtloZXAnEKKSget_WarningoBjFbI4u41epXVhNbFEget_LengthtQmThk45vJNYV8NvGfYget_Information1nN2HH42S2dJ37DXcRpxRh3T24A9gd32bjmGFHROrAac4LZ8qF6mknQFmTrimEndIJCqFu4bkDByxQnd2wGToLowersviOcN43W1fpyoo2k4tEqualsJ1gDh94QKK4V7a8cXrjeIp9ec47GN4BuwnKy3gDirectoryExistsGi02Rt4iJ3b4CZE8DyJ4huiqe4J3a4KD4f9esFop_InequalityZcR2qu4NrN8hZ6XEYkHkoXxwR4D2suWoRB1TZ4FileDialogset_FilterIndexSUiGPc4nODk2M7nEv6nset_RestoreDirectoryJd0REY4SxMoQHrF78CAset_Multiselectb74FxE4Or3JCyBJ3NoJset_FileNameExX7sX4r6X2aHw8L8KjYTyYjn4d94F48VZt09Aget_FileNames8C2KTG4WjZiiRM65AXsget_FileNamed5PIbM4KhN0ORPZL9ySQs0fng46e9E16qvxWpVset_VisiblevXf2Ou49hoixI3HAabTRefreshsOWVrb41ymyFeCioQI2ar5DKn4vSJsHQcV25oBuY6Q8O4sbc4a1xLSE4lset_TitleDT5Xa94EPbKlJjCTZHdset_Filterf9m1234FLRSU8UH1MmbFileSystemInfoget_CreationTime3A3CA94H44lusuY1j3VEvttVY4qMCxN86RANu5ListViewItemCollectionNQyomI4T2Zr9PscyigJget_Name0TAoGc48PAenoJdHFjRDteJkq4tLQNFfy9GGJwset_ImageIndexwVfpmi4zHsBO7Gt125v1QCPogCmCN25RCny37naqIKU2ColeVqBScTe8spGF0eGC4dHiWmkkAQjycApfpjCCmj7sd7I8t4Yset_AllowDropB0EJcmCRRq4HmZXJYutPushvZr3cHCZRcM6R7HP8Sce7q4YKCk2svjDrUdPmEPopKG0IlVClomTpgZKXjrYPeeksluwNZC0Zft3DV74xmeFileyA9Pm9CIaabOThN8hgOFocusyJZNrSCpMBM2aBeJRE7get_MessagemofpxXCMb6Cx6fx2QHlMoveNextvywtcECGXD3Z3u1FFwa17UOuDCxfnoPDE9swHHget_ColumnrkOYFVCf9QsJWDfGP00SortCYoql3CPDZJeycO2USCget_SelectedIndicesSelectedIndexCollectionolrGJ8ChNk81jfueM75GXN1QZCB2GXiFmq3IT4get_SelectedItemsSelectedListViewItemCollectionFyiSyvCyDbGaOBKaifywnvEClCeo7nr7K87xlAaJhIpJCXrRxodMfAL7JJ2UIEXCcmNTFUSqHdEGget_ForeColorrykpUlCgUAQivWOmK8Kget_SilverqKYgseCU3vYdyjrYeugqLtR4uCYQlDOMnjj1GWget_DataIDataObjecthbNWqrCVSFkGL6u2YwyGetDataNcOi2hCarW4FwNNboJQ9yKeI0Cw0kPWmlCZQ8OGetDataPresentV1kj93CjJ0QUc2uKxF3DragDropEffectsset_EffectOELvowCukdHS523bNUNget_KeyCharE1nniJC5xWnuNc7qElifHl3PFC2DZhZHHVjJR8RG5E5UCA14sRB7CYiv1suOoGXCLBLZZdjgXeCfu6NA4TCboqDj7fvJL6Pget_OKlHkTFvC38fajkJutiAMget_Canceluqr5HkCQchVdwhF4tiQget_Rename8pnOpcC7AmR0PWgeep4GQ7gm6CiKBXNJEi0XvyxLFimoCJK7HjrL2KQyaRuntimeFieldHandleRuntimeHelpersInitializeArrayArrayuiJCj6CN3KI6eMw8l0HIndexOfAnyYEkO2UCDUYkacJVBMx6get_BadCharactersr1ALZwCnp5gsTXtT7BgnWgvJmCSXpBFIwhUccIset_Focused8cJMnVCO8bXFMTawoYeT8Mlw4Cre3yuMVhEsRYClipboardContainsFileDropListjYW2fbCddyZolrXGMm3GetFileDropListvICLZRCW0bbx1tuIhnXyVmyu6CKgYSg2CqFstaVZQiwhC6d33uh32bjlPKkE4sLC9LpaOJKfBmyoCreateDirectoryDirectoryInfoIQWmKgC18h6EgAQYPkFget_NewFolderjATljnCvBZf3jcVNZt9c5vdRiCsjmmHLGZvLwoget_Chars2XgTGlCEn1nLoWEhPMnBeginUpdateRtkUgFCFtoyeUk01lS8set_SelectedBtNYXrCHgM27BKsw5QSEndUpdateGiln21CqQTbKLxiWEW6ClearyVH2XxCTE3X0le0VcbrGetLastWriteTimeuLsA1GC8MIy5fi6lMkvset_TagDfJalDCt1WvPr6uhu25ICollection`15t2jaeCz2GBHQopTe1UZXkLfERm48ZyvymYvbcQm6xqWRoN6xtsIfbYntQpo6rjR4FngQirtRTVgZUlN6pRCbfmga2pmU9rget_WantSolution1Bm42MRRrusLPjq5io4get_ConfirmSUX9lHRZCYYSvrZKqLCget_StartInfoProcessStartInfoJbI0uCRkOrgaOav2Xk6OqnBN3RlqCRWYPgVCdR9xZnCHR08KX0jXUaZI4ThreadSleepu5krvXRITTFr0viORGgExitlmaH20RptZ5GNaQ2qSxmeICveRM5mEDMbVTWMEget_NoDiscvoKXbVRGr877Zg2U0Haget_BurnerBusy70OcBaRxSjethnUIA9M0FAZ4mRfyEBHl5gbm6wEjectDisceJE5RDRP21Mwtgyujf4get_DriveBusyeRQIa1RhHhvakFy79q4OWhjMnRBfrfpgkVWXBRget_UserState98UxnmRyNmeUcqW5wTGget_ProgressPercentage7C6FV9ReFcko6wddXvKget_CancelledvtqCW0RXBB6F2coX33NMarshalSizeOfb0oPvWRcpxR6lWhPiJgFromHandleF6graXRg8ikHNVhuTWaCloneBMy8dsRUVeGCXKt2INJn6lrvHRYESWOF98oMXCIsBusywvAOX6RVysWDbUo6Jo6get_DragAndDropFilesHerepkS4SNRahqt38t9mYlEget_Red0R5OgVRwD5FdTecgKyXLf1txDRjJOWhD5aVIaQwFvDXBRuIZVUobcMHJh5uk67KR5EB7KYi2FNc5get_WhiteR527g1R2TG0uia3aWcTyuxY9KRAAI438gLk86Oget_LeftWHsdpIRLnnJWxbjttZJget_RightiPexBeRbPSqSSB10ctXget_Topu7keClR3imquGYUbyI0get_BottomaMRy7nRQexa9DG5rIunkPkrBFR7qMksqxVSd0DGetTypeBESV4KRiaklYlH1nG7ddBpDZwRJAt5KM1JoG2HvsO41sRNYWoVrs8YuFbget_DriveTB53tcRD0cl3ABiu0VMBringToFrontTBFjt5RnRWt2IwEuP9NSpecialFolderGetFolderPathORgHAyRSbUT2Stw1yG2get_ImagesImageCollectionbwV6CSROJcNETKwAWM9XPY24ORrXZAPtFFoCqwget_EmptyhDIjU9Rdsipw0EEZE55GetExtension83YucxRWEWMsIEinjUv2jksljRKPGn7CUyI0ExContainsKeyM8KkNTR6htyftRXbunao2DGNyR9jqFcR2DytR9IndexOfKeyyLqLD7R1xn8ZS6rr68sget_NowVGe3kiRvfv01Auo8wPyY7lx4mRsDIqibDLMmKejInvODREp782QLAp41DISupportInitializeBeginInitdqVJpARFufXAVYgTNOEVlqnIgRHEIkaA1EuANRpiOUFJRqXCtkWdlOgrn9fBMJSRTTh1vFoonHxkDirBwDR8ccf9MIGDglpmp4lKaRtIwciQkEMYqTt3qTChRzPFSYjrd48vi8RTxvDZmVVcULgBSVvou7bbOrZoXN5qvNKdR11I3UIEVZ4nQy3whq4Mw2FromArgboiUAMFZC9p2cFBOMoD6BorderStyleset_BorderStyleKKQpOhZRpG9w7UGRhnZset_MinimumSize0W9aAwZZN5370I3efKsget_SteelBlue4MZ7d9ZkNyWSMuPSYDyset_TabStop1W9Y0LZl4SahCK5nIddget_WindowOtWGHaZ0HwICC6qYG6n5N91DbZId2e5eIwISQgAddRangeCbPrLxZpWFwsBfXJAaDset_ContextMenuStripamPNRtZMIB0nygkkP2gset_FullRowSelectjihmV7ZGYS5ZML2g0Cmset_SmallImageListWu5ATfZxa08xG1mosV7set_UseCompatibleStateImageBehaviorIxKaSLZfqPeabQL5o47Viewset_View4k54QTZPFEfc9uGPLG0add_ColumnClick77yTAMZhJCOWdC7v4jgadd_DragDropUuF1iwZBDGF8uTCdTWQadd_DragEntervLyYUJZyRBdBBqTqupCadd_DoubleClickOkGRLAZe9jFcWOrWVsEadd_KeyPresspmfOdEZXPmGCPoHC8SuToolStripToolStripItemCollectionYGbsufZcrbIhKOmmEg1XKMR9LZgME0dEQ7HVUrToolStripDropDownadd_OpeningXk9hdbZUfAMW8mrXUlQe5XhUZZYS9OKPvHCLui3h2oPfZVvyan7Y9yjWDToolStripDropDownItemget_DropDownItemsC8MaaJZabmS8eWcjl7MColorDepthset_ColorDepth5ds1gJZwpAdOdP3AVBoset_TransparentColorKO5M7RZj9tvelE2hY4MLZGZsTZuAw2RwSv1tpDc9OK4mZ5FGYVQk9pnp0X5ZgtjZ2GyLM5TQQMQiget_ControlText4DRBVBZAa89AvCVjs2mget_Maroont9pm9qZL2NS7VOicoQJMPWW98ZbtJ5TyWMrTu4AeIkInZ3U0a48l9HyPIsFPrJkZQOjbZwuL8cpxadd_FormClosinge2X2yqZ7koBKdOZaAOoEMDrZXZi9aC8YgxgnYSadd_ShownBCKaPuZJSfHkn4u7NIvadd_ResizeIPPeoSZN4FiLbFyXTHcUWB2jkZDG63FTHNNDEDv7hjErZnO1c57JFBja7EndInit5LKtLJZSBgdWxa8gnVJRecorderIndexDiscTitleSpeedIndexCDSpeedDVDSpeedEjectFinalizeDiscISO9660CheckJolietCheckUDFCheckTempDirectoryWindowStateSortTypeIndexSortReversColumnWidthSplashErrorReportFormBitmapStreamShowSplashSetSplashGetHumanNameGetProgramPageUrlSubstringset_ProgramPageUrlSubstringIWin32WindowCaughtAndHandledExceptionYlZPbCZ9JH4UoSikBYBEnableVisualStyleseLHrKiZ1YxbTWXLx3MpSetCompatibleTextRenderingDefaultIWfYA1ZvMOCwGFmMh8ZwM7usJZsgfGxP2Uk2QdGetExecutingAssemblyO7RY8AZEXFgGWMJpkF5GetNameAssemblyNameQasp5xZFkFXJTPL1wQAN84retZH6bTn0TneVrcoFG99TZqcngLkSGbntvJovZKnZTsPbApYuavlLGetManifestResourceStreamcobZZ2Z8rrKwMxhQHdJClosehppi7jZtuhNlhmkWYnO2p7dvGZzJGQh5KDTT0FuZUvrGkmCX7X0IiNiVERun6cr3pLZKP8FoVgyb1AZVPSuVFZ6KdwsEoxkuRQget_backcolorget_MainIconget_ProcessIconget_Success1AD2l9kCxDniVixaws6mmq4WqkRrId4gXBnu71sV477bko8hZpDc5DrYhFmvLa5k47hl2rmMrCjmU5f8wTkZrcDU3mYsZX0hbkmRMkkKXfOLbYD0GyqZrhaPklHb3EbWWYWQWGetObjectbackcolorMainIconProcessIconSuccessdefaultInstanceget_Default.cctorP9WBx3kpmTdX1NgtHnS1xVOChkMxYkDAV5w6woH3ec1Nk09ywBM5Pm6YO67SEhDkIrC0JEAXMmphQWa6mGkGonDlZV54NgRSettingsBaseSynchronizedDefault$$method0x6000052-1$$method0x6000056-1ij0fPFSST4bmVtypemdtFieldInfoMethodInfoGetFields9TcgGukcL7ZuWN6c9ykModuleResolveTypeHEWcQ1kgixdWCRBF6vPMemberInfoget_MetadataTokenwokhWEkUNk9HuRAGLt2ResolveMethodMethodBaseagan3YkYHZf6MWC40t4CreateDelegateU8kwD3kVGlA59Uat54eSetValuegeF8OHkeUbFuoFevHrXyZ4prKkXVqOis7Db7ATlB5dwbkahbkcrHXyRaGX2H58pkw1B5DfwsGZwNc6tsmskjglssXWprJOrJyjFy1kufMi4UbD6XxQget_ManifestModuleobjectmethodoBeginInvokeIAsyncResultAsyncCallbackcallbackEndInvokeresultDictionary`2ByteZeroSortedListHl2fPFS7Kt8UPCryptoStreamSystem.Security.CryptographyBinaryReaderICryptoTransformMemoryStreamRijndaelManagedCryptoStreamModeTrimFromBase64StringEncodingSystem.Textget_UnicodeRtlZeroMemorykernel32.dllVirtualProtectFindResourceRSACryptoServiceProviderset_UseMachineKeyStoreget_Locationget_CodeBaseReplaceGetPropertyPropertyInfoGetValueLoadLibrarykernel32GetProcAddressWriteProcessMemoryReadProcessMemoryOpenProcessCloseHandleFileStreamFileModeFileAccessFileShareReadRijndaelCreateSymmetricAlgorithmset_Keyset_IVCreateDecryptorWriteToArrayha01grC4JXN2Uqr2MKcQjZnQBV1jGYYw7RELQcZQxnZs0BqAQQfmxmget_BaseStreamyWHcNB7XvV1PxdHa1Xset_PositionFHAIx3lSrGvkq2b1bueDd4ML0SJCvAfJ1koCEJJVOjYrJSJn4VNW16ReadBytesO50qtqcmbNArjMh0cSjI0DVKMLCOkNwvEdO0GetPublicKeyToken1JciIg8QKTGPAmpWiyCipherModeset_ModeHRGtKQns9cP3hRuITtm38gbT6OXwvS4bmSfrMyVHSjKkqkDweJBKljFlushFinalBlockLbiyerkW0XoiPZK5oWffZXESs4wZUtFM2gYgrLFpUnJdMLcSX8XO14JN93CiI3QAgex6MxhtBitConverterToInt32bRwfGdQL09JcHt1RBAop7am1w4BpLWDlnMxUdaLj5coEvGdnXc6ottBVjSx742YLEqgZdWUnmo40Wvkbc783wEKaawMxpHdXhk32Td3qutDpQTGGh53ckQqqXb4PuuYhMampwR1k78ItGPfYn13sTce4DmfsmSrOT856tDgfrkMbMD5MD5CryptoServiceProviderntKnRpkNS9BfMRHvY8Mab8AlekDb9TH043iy3gh0AnWUknSc2Sk3pNgUgAZ1ue5kSLAXPe7Z6NpLToBase64StringlgtGmskOYR8OrPIrIxkCfMBcUkr09SK5qttix8HmeglSkdvmNPKn7CyMETpHMMmkWtmJB8PCJnuJOeWXq5kKwy6NQKV9otRGetByteskk3oLAk62bvq6S1vy5U9Bh4dZk91vTJ797GSvSHashAlgorithmComputeHashoghHWnk1wN97hUEND5Op6dZVGkvwuglqnwGh8NSVaPNvksWXkm8qJgQQ5J0illukEQBpDQojemcPCreateEncryptork4ZQZvkFZUWay5XAX39xNWoS8kHMHHr06DcZVqRdodyNkqxfX3PVNxFO8aVBgrikTHyGM3mMA7sWU7KjQtk80PMFB0LSpo1UInt64BLSfPFSzsDwJNApIEQGl0oLXv0FlZWmbAppDomainResolveEventHandlerlLHifFIsCLsZtjvFfN0iResolveEventArgsHashtablehsnrl8lM9CSXQoWGemUlPm2tvlGCBHrIgefkuHget_CurrentDomainFTxbnplx12J1IQ6b4lKadd_AssemblyResolveJLWjjHlfeUkZ6XLuDuRi8a6G7lPR5smVx5kVoPRGfNbelhyRaYudrTlpUn5Np5XlBkFxmn2u4Yp8V0giKdlya250c17voElhWcdT8let4Kvg2c1oB4Jf4kU0lXGHE5AITq0OANfN9I6lcQcuNaXgI1HoNA7Cdllg0wmNFxFmIYMZRLCLOlUvwKYmMt8KVxQXl1rdlYgNNnWXNm22hS6lGC4lV1SiCaQDfPvP4wEcIHlaWJSNXA4i0dKIO4xUXlwWBDKlMejgr0XYOudyljTIodwyctRXhu5wjqbluAjOOETSv4mrNW6ONVl5AUfMPr1267kLoadSk8GbBl2t16A5WELPEqget_FullNameIJRYSnlA3S9O8silgkjbBZ0n0lLGFwloEP9y6dIndexOfp9GlXVlbKAVeQMjwYvWSubstring$$method0x600001d-1$$method0x600001d-2$$method0x600002c-1$$method0x600004e-1$$method0x600026c-1$$method0x600026c-2F9XKeOl6vlxourFpsvQFreeDiscBurner.OptionsForm.resourcesFreeDiscBurner.StringResources.resourcesFreeDiscBurner.FormDialog.resourcesFreeDiscBurner.Main.resourcesFreeDiscBurner.Splash.pngb0494a1f-4bd3-OQGXfPVcwg2d75cuk72jxA==b0494a1f-4bd3-8hSeRGHzYDNVylaB3fVqJw==b0494a1f-4bd3-WdfsM593UCNoh0skiaZkSA==b0494a1f-4bd3-kALMiogkM4LGqE/oOd133w==b0494a1f-4bd3-B8e77awqlfNfi73OsSxdlw==b0494a1f-4bd3-R8THV6v22cqqeGO/hwTzqQ==b0494a1f-4bd3-ZE393gnYXN7OQ1PNT58exA==b0494a1f-4bd3-C644nPIhe0AMNlVlKwsM+A==b0494a1f-4bd3-b36YZ1u0g+/+nDMS2fpicA==kKVTf7nGiopt385jtF.BCmqKe3r570WwZbTqSb0494a1f-4bd3-hCcw3DrxsSV6N7tUvMVbVg==b0494a1f-4bd3-5sEs3rs4gds65/u5layTQw==aR3nbf8dQp2feLmk31.rFohpatkdxsVcxLfJKhM7.resourcesaR3nbf8dQp2feLmk31.lSfgApatkdxsVcGcrktoFd.resourcesFreeDiscBurner.Properties.Resources.resourcesCompilerGeneratedAttributeGeneratedCodeAttributeSystem.CodeDom.CompilerDebuggerNonUserCodeAttributeEditorBrowsableAttributeEditorBrowsableStateUnmanagedFunctionPointerAttributeCallingConventionFlagsAttributeSTAThreadAttribute=FreeDiscBurner.StringResourcesGFreeDiscBurner.Properties.ResourcesKkKVTf7nGiopt385jtF.BCmqKe3r570WwZbTqS3{11111-22222-50001-00000}file:///Location3{11111-22222-20001-00001}3{11111-22222-20001-00002}3{11111-22222-30001-00001}3{11111-22222-30001-00002}3{11111-22222-40001-00001}3{11111-22222-40001-00002}3{11111-22222-50001-00001}3{11111-22222-50001-00002}QIIMۑz\V4TWrapNonExceptionThrows   Free Disc BurnerDVDVideoSoft Ltd. Free Studio)$Copyright Вc DVDVideoSoft Ltd. 2010 )$75960d3a-79e1-4dbd-b2aa-948c5e9df486 3.0.6.920 en-US MQzyara-4.1.3/tests/data/3b8b90159fa9b6048cc5410c5d53f116943564e4d05b04a843f9b3d0540d0c1c000066400000000000000000000533101413423160300250070ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PEd `"    `q@@@ @`"4 H.text  `.rsrc`@@@.reloc`"@BH7P AlSystem.Resources.ResourceReader, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSet%PADPADPiEx['x q4`t?|iy%F nn[&%*O0--3g4x=k><2/?T3CDB/FyH@MGY"au%bhJ>v'{X   KAo"GOWuj- + pCanOnlySetFocusScopePriorityOnAnElementThatIsAFocusScope@CannotAddAtomicUnitToUndoManagerHlCannotAddUndoUnitsToAnUndoContainerThatIsAlreadyClosedPCannotClearFromWithinAnOpenUndoContainer8CannotCreateWorkspacesFolder8CannotEndClosedUndoContainerBCannotEndUndoContainersOutOfOrderBCannotEndWithoutOpenUndoContainerFCannotModifySealedUndoUnitContainerCannotNestAdds$NCannotRedoFromWithinAnOpenUndoContainer~NCannotUndoFromWithinAnOpenUndoContainerRCantRegisterADependencyOnANonexistentTaskXtCantRegisterADependencyOnATaskAtALowerPriorityThanYourself@CantRegisterTwoTasksWithSameNameJCantRemoveATaskThatOtherTasksDependOncTConvertBackNotImplementedOnMarginConverterLConvertNotImplementedOnMarginConverter&DocumentAlreadyOpen+$DocumentIsReadOnlyVJDocumentNotIncludedInOpenDocumentListDError_ConverterFunctionNotDefined1DError_InsufficientSourceParameters@Error_InsufficientTypeParameters*HError_TargetAtOffsetNotExtendingTypeQ:Error_TargetNotExtendingType18Error_ValueAtOffsetNotOfType*Error_ValueNotOfType10InvalidTargetSubPropertyBMethodOrOperationIsNotImplemented6HNoConvertBackForValueToIconConverterm RedoStackIsEmpty UndoStackIsEmpty&UnexpectedBrushType*UnexpectedDrawingType2UnexpectedImageSourceType ViewAlreadyOpen> FFocusScopePriority lässt sich nur für FocusScope-Elemente festlegen.fEine Rückgängig-Einheit des Typs "Atomic" lässt sich nicht zur Rückgängig-Verwaltung hinzufügen.fRückgängig-Einheiten lassen sich nicht zu bereits geschlossenen Rückgängig-Containern hinzufügen.eDer Rückgängig-Stapel lässt sich nicht aus einem geöffneten Rückgängig-Container heraus leeren.BFehler beim Zugriff bzw. Erstellen des Arbeitsbereich-Ordners: {0}REin geschlossener Container für Rückgängig-Einheiten lässt sich nicht beenden.iContainer für Rückgängig-Einheiten lassen sich nicht beenden, wenn ihre Reihenfolge durcheinander ist.RBeenden ist ohne geöffneten Container für Rückgängig-Einheiten nicht möglich.LVersiegelte Container für Rückgängig-Einheiten lassen sich nicht ändern.XAufrufe für das Hinzufügen von Rückgängig-Einheiten lassen sich nicht verschachteln.sWiederherstellen/Wiederholen ist aus einem geöffneten Container für Rückgängig-Einheiten heraus nicht möglich.cRückgängig ist aus einem geöffneten Container für Rückgängig-Einheiten heraus nicht möglich.ZEine Abhängigkeit lässt sich nicht für Aufgaben registrieren, die nicht vorhanden sind.hEine Abhängigkeit lässt sich nicht für Aufgaben registrieren, deren Priorität geringer ist als Ihre.CZwei Aufgaben mit identischen Namen lassen sich nicht registrieren.TAufgaben lassen sich nicht entfernen, wenn andere Aufgaben von ihnen abhängig sind.9ConvertBack ist für MarginConverter nicht implementiert.5Convert ist für MarginConverter nicht implementiert.)Das Dokument "{0}" ist bereits geöffnet.)Das Dokument "{0}" ist schreibgeschützt.NDas Dokument "{0}" ist nicht in der Liste der geöffneten Dokumente enthalten..{0} ist für diesen Konverter nicht definiert.'Converter erfordert {0} Quellparameter.%Converter erfordert {0} Typparameter.>Zieltyp bei Versatz {0} muss sich über {1} hinaus erstrecken..Zieltyp muss sich über {0} hinaus erstrecken.2¬Wert bei Versatz {0} muss den Typ {1} aufweisen. Wert muss den Typ {0} aufweisen.Ungültige TargetSubProperty.5Die Methode oder der Vorgang ist nicht implementiert.,Kein ConvertBack für ValueToIcon-Konverter. Der Wiederholen-Stapel ist leer.!Der Rückgängig-Stapel ist leer.Unerwarteter Pinseltyp {0}.Unerwarteter Zeichnungstyp {0}.Unerwarteter Bildquelltyp {0}."Die Ansicht ist bereits geöffnet.lSystem.Resources.ResourceReader, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089#System.Resources.RuntimeResourceSetPADPADPQ-iG֮+RSNDocumentGroupTabItem_CloseButtonToolTip:StudioBook_CloseButtonToolTipDStudioBook_CloseGroupButtonToolTip'Dokument schließenPanel schließenEGruppe schließen ("Umschalttaste" halten = Aktives Panel schließen)BSJB v4.0.30319l#~#Strings#US#GUID d#Blob %3%B_x <<<!<)<1<9<. $.7.7.#$.+=.37.;U  (UmscorlibSystem.ReflectionAssemblyTitleAttribute.ctorAssemblyDescriptionAttributeAssemblyCompanyAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyTrademarkAttributeAssemblyFileVersionAttributeSerif.Windows.resources.dllSerif.Windows.resourcesdeSerif.Windows.Resources.ExceptionStringTable.de.resourcesSerif.Windows.Resources.StringTable.de.resources ^,xNVpZ3z\V4?OD ?YZ   Serif.WindowsCopyright © 2014 1.9.1.9430HX@4VS_VERSION_INFO  ?DVarFileInfo$TranslationdStringFileInfo@000004b0DFileDescriptionSerif.Windows4 FileVersion1.9.1.943XInternalNameSerif.Windows.resources.dllHLegalCopyrightCopyright 2014`OriginalFilenameSerif.Windows.resources.dll<ProductNameSerif.Windows8 ProductVersion1.9.1.943< Assembly Version1.9.1.943404 *H 4041 0 +0L +7>0<0 +70 0!0 +ܘXz@3Sg&A v00Ơ+VQZG|<}=0  *H  01 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA0 190411000000Z 220517235959Z0v1 0 UGB10U Nottingham10U Serif (Europe) Ltd10U Serif (Europe) Ltd10U Nottinghamshire0"0  *H 0  *[1 ̬# FV;"8A8# <5V8yYUe[zʯ#,LH[S(z4ӀehK8^K/Wn[m\X~Zj՝Qh,׆[|~1i\bh,G]iFVV(T2+a]JN,Gٸ=_S.0ѵnRv'Ĺ]0Y0 U00U0+U$0"0 http://sv.symcb.com/sv.crl0aU Z0X0Vg 0L0#+https://d.symcb.com/cps0%+0 https://d.symcb.com/rpa0U% 0 +0W+K0I0+0http://sv.symcd.com0&+0http://sv.symcb.com/sv.crt0U#0;Sy3}.+ʷrf0U喌sw@WDb)mp0  *H  1gtMP /Y>W~t %0bMްym.1Rݠ -0ޢ{^7#):Qĥ[0;ǭC ~#[pY,[GdS<8/p9q]r =mX4;;3`<$(Yq1(->_!5d&Lu51/]kEx"}" «m0:n[ėKc00 BJ:`@!0  *H  0r1 0 UUS10U  DigiCert Inc10U www.digicert.com110/U(DigiCert SHA2 Assured ID Timestamping CA0 210101000000Z 310106000000Z0H1 0 UUS10U DigiCert, Inc.1 0UDigiCert Timestamp 20210"0  *H 0 agŊ EckQ:B uuM P&ok \HD忛abZg c6j)+pxm}m =jv銽]vvv:SZ6ƣ/:asNm3|]E-KPA:7Vܓ#P%dӏ/W ,#MxF1)J\ 1J0Z_lCI &#Sc[0›iVTz[ /K ɕcNrzs I00U0 U00U% 0 +0AU :0806 `Hl0)0'+http://www.digicert.com/CPS0U#0 )a%5n0U6Df(-D6jz0qUj0h020.,http://crl3.digicert.com/sha2-assured-ts.crl020.,http://crl4.digicert.com/sha2-assured-ts.crl0+y0w0$+0http://ocsp.digicert.com0O+0Chttp://cacerts.digicert.com/DigiCertSHA2AssuredIDTimestampingCA.crt0  *H  Hܵ# gFBrQt #G%pY'MyÆ;DfMipbQJxy6z'RC{A"FNdwc_#RiS̍s e9pKz{ =2auv\rdI"uB-28`R\a$%O)sCQڽc[.OJ q~?IΧVϺYva_~gx6([/010 %2~A60  *H  0e1 0 UUS10U  DigiCert Inc10U www.digicert.com1$0"UDigiCert Assured ID Root CA0 160107120000Z 310107120000Z0r1 0 UUS10U  DigiCert Inc10U www.digicert.com110/U(DigiCert SHA2 Assured ID Timestamping CA0"0  *H 0 2K͏ݩ9T(W#JE3Q}}Mh~綠􍳈䗿c!W~j8.;̦2Y_e?FGU~ '~"?~">m;| ]Ț T qk"uqrN @:\QyM$#0]<`j/GوP^fnVa')QFP*,u>1֭d{Єr=MBS*O<ڬݬV00U )a%5n0U#0E뢯˂1-Q!m0U00U0U% 0 +0y+m0k0$+0http://ocsp.digicert.com0C+07http://cacerts.digicert.com/DigiCertAssuredIDRootCA.crt0Uz0x0:864http://crl4.digicert.com/DigiCertAssuredIDRootCA.crl0:864http://crl3.digicert.com/DigiCertAssuredIDRootCA.crl0PU I0G08 `Hl0*0(+https://www.digicert.com/CPS0  `Hl0  *H  qQVi|czx7@Kŧ B,w+ !Z*4j'f `v:|b^%ĺ{,![,5Churǘ٦V?.)|=꺓P")L:_֤%k/L'{ "`?MLrgw'Ǻ5I(J D 6+P]'KT+^t É"wCL?d!10001 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA+VQZG|<}=0 +p0 +7 100 *H  1  +70 +7 10  +70# *H  1;-hRa֐I}`0  *H 0C"ܦ>OxRCkD"Fr4Ńc_y%6?I^%;'{\GSn}q4!#WDoz<ӥAkkkK9 G%}/965si8::Ltw8X ^ 7] [ZXi&xmXdDY> ?00Ơ+VQZG|<}=0  *H  01 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA0 190411000000Z 220517235959Z0v1 0 UGB10U Nottingham10U Serif (Europe) Ltd10U Serif (Europe) Ltd10U Nottinghamshire0"0  *H 0  *[1 ̬# FV;"8A8# <5V8yYUe[zʯ#,LH[S(z4ӀehK8^K/Wn[m\X~Zj՝Qh,׆[|~1i\bh,G]iFVV(T2+a]JN,Gٸ=_S.0ѵnRv'Ĺ]0Y0 U00U0+U$0"0 http://sv.symcb.com/sv.crl0aU Z0X0Vg 0L0#+https://d.symcb.com/cps0%+0 https://d.symcb.com/rpa0U% 0 +0W+K0I0+0http://sv.symcd.com0&+0http://sv.symcb.com/sv.crt0U#0;Sy3}.+ʷrf0U喌sw@WDb)mp0  *H  1gtMP /Y>W~t %0bMްym.1Rݠ -0ޢ{^7#):Qĥ[0;ǭC ~#[pY,[GdS<8/p9q]r =mX4;;3`<$(Yq1(->_!5d&Lu51/]kEx"}" «m0:n[ėKc0Y0A=xvI`a}ʆ*0  *H  01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CU٦V?.)|=꺓P")L:_֤%k/L'{ "`?MLrgw'Ǻ5I(J D 6+P]'KT+^t É"wCL?d!10001 0 UUS10U Symantec Corporation10U Symantec Trust Network100.U'Symantec Class 3 SHA256 Code Signing CA+VQZG|<}=0  `He0 +7 100 *H  10 *H  1  +70 +7 10  +70/ *H  1" 1 ؚEU1AEBBS@03r0  *H U9wOXmsU=EW&t6wIeN@N7DOk~Efo߽d> VND-8e"5:k݋S">9Yt~~ϒ㷶58ȎpURv[43=ZH:W?بҖ2=*{bM@?<Ei3,p@TvxEEIwV0j q[bϳ^PVDV'Psa‘5ھƗ:9i<08 +71(0$ *H 01 0  `He0 *H  0 `HE010  `He :'aɭ7ć[ Y4)+fdqN9fE|;20210206230923Z001 0 UUS10U Symantec Corporation10U Symantec Trust Network110/U(Symantec SHA256 TimeStamping Signer - G3 080 {IhQDɉҜ0  *H  01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2008 VeriSign, Inc. - For authorized use only1806U/VeriSign Universal Root Certification Authority0 160112000000Z 310111235959Z0w1 0 UUS10U Symantec Corporation10U Symantec Trust Network1(0&USymantec SHA256 TimeStamping CA0"0  *H 0 YYUOr]UM 3I;Kሙ3ǀۑWrwͨ 7jK _y5x2#2AY>+m:ɜHS6xš7w0s0U0U00fU _0]0[ `HE0L0#+https://d.symcb.com/cps0%+0https://d.symcb.com/rpa0.+"0 0+0http://s.symcd.com06U/0-0+)'%http://s.symcb.com/universal-root.crl0U% 0 +0(U!0010UTimeStamp-2048-30UcʣNrৼA)8ub0U#0wiHGS2vї0  *H  u-4\2EOxoOȁ@)]Vdq0*af V]q G̐/ lN3ִLcH9@!n< ɏSWA/MNxlBna\RNkJzS uu.fW:Ί۞8R` M'm2?@Ῑ9+ZXU}ǚ#*a0K03{寺?#"AM0  *H  0w1 0 UUS10U Symantec Corporation10U Symantec Trust Network1(0&USymantec SHA256 TimeStamping CA0 171223000000Z 290322235959Z01 0 UUS10U Symantec Corporation10U Symantec Trust Network110/U(Symantec SHA256 TimeStamping Signer - G30"0  *H 0 ܿi{TqG e$ m0?'J3Nm@" <$(rQ= &:C#uGp _[rHWeM<@28?,Y#ˀMɪ lUaW,ĵl#ҟ lbiNR1[tRWl"Uo?J"ve6򖕁&,8ӯ%@ S)WX^HA!޴&{@SXyR1ME<:"Ŵoﰊ00 U00fU _0]0[ `HE0L0#+https://d.symcb.com/cps0%+0https://d.symcb.com/rpa0@U9070531/http://ts-crl.ws.symantec.com/sha256-tss-ca.crl0U% 0 +0U0w+k0i0*+0http://ts-ocsp.ws.symantec.com0;+0/http://ts-aia.ws.symantec.com/sha256-tss-ca.cer0(U!0010UTimeStamp-2048-60Un_;1y6{0U#0cʣNrৼA)8ub0  *H  F_HϨ'K!Ͳrpc/U){9FN~eXW 6$ {;bJۊTm(gq?3 ٢YJekR)uJ+˛MYA÷?Dz"tG %t`n`Vz DXHwr$,?Kg_\T>3[ŷNqfE?v1Z0V00w1 0 UUS10U Symantec Corporation10U Symantec Trust Network1(0&USymantec SHA256 TimeStamping CA{寺?#"AM0  `He0 *H  1  *H  0 *H  1 210206230923Z0/ *H  1" L%<(hnr#3c4#eM07 *H  /1(0&0$0" tv}9N |ƀ^86ADOhP zf7P+Wjeٙk.ﲂ3y lzhT:h9v s;/|}-ƅ]f}zYh6mͪ)LQ8YmSLT>m2ubҿk$|GTd-"| [RLJ G`ܙp|*1+.; &pom7&qyara-4.1.3/tests/data/base64000066400000000000000000000034241413423160300155350ustar00rootroot00000000000000Plaintext Encoded Substring This program cannot VGhpcyBwcm9ncmFtIGNhbm5vdA== VGhpcyBwcm9ncmFtIGNhbm5vd AThis program cannot QVRoaXMgcHJvZ3JhbSBjYW5ub3Q= RoaXMgcHJvZ3JhbSBjYW5ub3 AAThis program cannot QUFUaGlzIHByb2dyYW0gY2Fubm90 UaGlzIHByb2dyYW0gY2Fubm90 These are the wide versions of the string. To make it easier I'm splitting them each onto their own line. This program cannot VABoAGkAcwAgAHAAcgBvAGcAcgBhAG0AIABjAGEAbgBuAG8AdAA= VABoAGkAcwAgAHAAcgBvAGcAcgBhAG0AIABjAGEAbgBuAG8AdA AThis program cannot QVQAaABpAHMAIABwAHIAbwBnAHIAYQBtACAAYwBhAG4AbgBvAHQA QAaABpAHMAIABwAHIAbwBnAHIAYQBtACAAYwBhAG4AbgBvAHQA AAThis program cannot QUFUAGgAaQBzACAAcAByAG8AZwByAGEAbQAgAGMAYQBuAG4AbwB0AA== UAGgAaQBzACAAcAByAG8AZwByAGEAbQAgAGMAYQBuAG4AbwB0A These are the ascii strings converted to base64wide VGhpcyBwcm9ncmFtIGNhbm5vdA== QVRoaXMgcHJvZ3JhbSBjYW5ub3Q= QUFUaGlzIHByb2dyYW0gY2Fubm90 These are the wide strings converted to base64wide VABoAGkAcwAgAHAAcgBvAGcAcgBhAG0AIABjAGEAbgBuAG8AdAA= QVQAaABpAHMAIABwAHIAbwBnAHIAYQBtACAAYwBhAG4AbgBvAHQA QUFUAGgAaQBzACAAcAByAG8AZwByAGEAbQAgAGMAYQBuAG4AbwB0AA== Encoded with a custom alphabet (still using = for padding): !@#$%^&*(){}[].,|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu E&QYLh@fLVsWLV^c(&]QKVoeM!== |EAXJG[PL*)eIm)QKB@SHFodKm|= |D^DJ&Ui(*@hKlMhHFjPHl^dKVsj Encoded single byte (a) base64, second encoding is skipped YQ== AGE= AABh Encoded single byte (a) base64, second encoding is skipped YQ== AGE= AABh yara-4.1.3/tests/data/baz.yar000066400000000000000000000026401413423160300160160ustar00rootroot00000000000000/* Add padding for making the file large enough to trigger issue #884 pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading pading */ rule baz { condition: true } yara-4.1.3/tests/data/foo.yar000066400000000000000000000000671413423160300160260ustar00rootroot00000000000000include "include/bar.yar" rule foo { condition: bar } yara-4.1.3/tests/data/include/000077500000000000000000000000001413423160300161465ustar00rootroot00000000000000yara-4.1.3/tests/data/include/bar.yar000066400000000000000000000000621413423160300174250ustar00rootroot00000000000000include "../baz.yar" rule bar { condition: baz } yara-4.1.3/tests/data/mtxex.dll000077500000000000000000000230001413423160300163630ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $_'K>IK>IK>I.XJI>I.XMA>IK>HW>I.XHN>I.XIJ>I.XAI>I.XJ>I.XKJ>IRichK>IPEd3l"   p`A&&<P@`p"T !.textP  `.rdata @@.data0@.pdata@@@.rsrcP @@.reloc`$@BH(uaH(H\$Ht$WH H=Y%IHHuK3H AHHuH# N\3H %tH%Hu.H $HHHt3H $H$LHH H\$0Ht$8H _H(H$H$HuH 3H(H\$Hl$Ht$ATAVAWH 3MLvDeH%0HpH;t3H5$uA$tH-#HtdH5#LLHH;r7HHtHH #H#L;uL;tLHLHHH{#H|##EHn#eH%0HpH;t3H54#u-#t _H5V #L%UI;s#uH9t HHI;rt3gH H "u HH"H9"t(H "tH"MƺIH\$@Hl$HHt$PH A_A^A\H\$Ht$WH IHuLNjHH\$0Ht$8H _HHX L@PHHVWAVHPLމ\$ ;wu9u 3ۉ\$ B;L !MtB;DƉL$I ؉D$ 3ۉ\$ s$xL$pyL$I؉D$ 3ۉ\$ s$xL$p?L$II؉D$ 3ۉ\$ s$xL$p;E33I $xL$p\$ E33I$xL$p\$ H Ht(E33I $xL$p\$ turL$I؉D$ 3ۉ\$ s$xL$pH% Ht4=et+L$I ؉D$ 3ۉ\$ s$x;w H$HPA^_^ffH; uHfuHR%n % LcA%%D%L%% "%0%%,%,%%1%1%% %0P%0 H`Pxp4VS_VERSION_INFO @* cE?StringFileInfo040904B0LCompanyNameMicrosoft Corporation2FileDescriptionCOM+v+FileVersion2001.12.10941.16384 (WinBuild.160101.0800)4 InternalNameMTXEX.DLL.LegalCopyright Microsoft Corporation. All rights reserved.< OriginalFilenameMTXEX.DLLj%ProductNameMicrosoft Windows Operating System> ProductVersion10.0.17763.1DVarFileInfo$Translation  hyara-4.1.3/tests/data/mtxex_modified_rsrc_rva.dll000077500000000000000000000230001413423160300221240ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $_'K>IK>IK>I.XJI>I.XMA>IK>HW>I.XHN>I.XIJ>I.XAI>I.XJ>I.XKJ>IRichK>IPEd3l"   p`A&&<P@`p"T !.textP  `.rdata @@.data0@.pdata@@@.rsrcP @@.reloc`$@BH(uaH(H\$Ht$WH H=Y%IHHuK3H AHHuH# N\3H %tH%Hu.H $HHHt3H $H$LHH H\$0Ht$8H _H(H$H$HuH 3H(H\$Hl$Ht$ATAVAWH 3MLvDeH%0HpH;t3H5$uA$tH-#HtdH5#LLHH;r7HHtHH #H#L;uL;tLHLHHH{#H|##EHn#eH%0HpH;t3H54#u-#t _H5V #L%UI;s#uH9t HHI;rt3gH H "u HH"H9"t(H "tH"MƺIH\$@Hl$HHt$PH A_A^A\H\$Ht$WH IHuLNjHH\$0Ht$8H _HHX L@PHHVWAVHPLމ\$ ;wu9u 3ۉ\$ B;L !MtB;DƉL$I ؉D$ 3ۉ\$ s$xL$pyL$I؉D$ 3ۉ\$ s$xL$p?L$II؉D$ 3ۉ\$ s$xL$p;E33I $xL$p\$ E33I$xL$p\$ H Ht(E33I $xL$p\$ turL$I؉D$ 3ۉ\$ s$xL$pH% Ht4=et+L$I ؉D$ 3ۉ\$ s$x;w H$HPA^_^ffH; uHfuHR%n % LcA%%D%L%% "%0%%,%,%%1%1%% %0P%0 HAXSxp4VS_VERSION_INFO @* cE?StringFileInfo040904B0LCompanyNameMicrosoft Corporation2FileDescriptionCOM+v+FileVersion2001.12.10941.16384 (WinBuild.160101.0800)4 InternalNameMTXEX.DLL.LegalCopyright Microsoft Corporation. All rights reserved.< OriginalFilenameMTXEX.DLLj%ProductNameMicrosoft Windows Operating System> ProductVersion10.0.17763.1DVarFileInfo$Translation  hyara-4.1.3/tests/data/test-pb.data000066400000000000000000000011151413423160300167320ustar00rootroot00000000000000# # Data used by test-pb.c. # # The structure for this data is defined in: # libyara/modules/pb_tests/pb_tests.proto # # Use the command below for encoding the data in binary form: # protoc -I./libyara/pb -I./libyara/modules/pb_tests --encode=test.RootMessage \ # libyara/modules/pb_tests/pb_tests.proto < tests/data/test-pb.data > tests/data/test-pb.data.bin # f_int32: 1111 f_int64: 2222 f_string: "foo" f_struct_array: [ { f_string: "bar" f_enum: 1 }, { f_string: "baz" f_nested_struct: { f_int32: 3333 } } ] yara-4.1.3/tests/data/test-pb.data.bin000066400000000000000000000000401413423160300174750ustar00rootroot00000000000000bfoor barr bazyara-4.1.3/tests/data/tiny000066400000000000000000001000001413423160300154200ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PELVV  p0@ܨ `a.textt `P`.data000@0.rdataP@@@0@.bssP`.idata`P@0.CRT4p`@0.tls p@0Í&'1f=@MZS@S@S@$P@thP@S@tJ$$ S@S@S@a@=0@tm1Í&$`f<@@PE@uQf t?f j]1Kv$@1Ãyt,1f,S@D$P@D$P@D$P@$P@P@ P@D$ ,fU1WVUS׃|0)čD$@@@ @@@@̃5S@d1X=@a@9$׃S@uޡS@1ۃS@MP@S@,@@tD$D$$Ѓ b $@ vU,0@t<$@@a@tD$@@$ a@…t $,0@$`@Í&U]ÐUu*ÐffffffS(S@$D$$*S@$YD$S@$HD$D$D$D$D$D$0$$ËD$$*S@D$$$S@d([ÐD$0$a@([Í&'D$ $1Ð0@t fС0@P@0@u Ít&S`'@t!t `'@u$p@[1ÍC`'@uɍv'P@tÐt&P@딐%ha@1ÐUWVS,$0@D$D$=N@tУ(0@,[^_]ÍD$$(a@\$3\$a@a@,a@ƍD$$8a@3\$3\$111N@tЉ5$0@(0@,[^_]ÐDO@t&U( S@ EU$S@$Q@P@,S@EP@$0@E(0@EH)ЉẺiËŋE)ljẺN>뮋 S@AD$A@$A@D$D$$A@US]=wK==D$$ t-$ c'=t)==tWS@t=]]t&D$$pt$и]v'D$$0uD$$~=jD$$tXI$|fD$$a&D$$ A&D$$!UWVS$S@a@S@-Ha@=a@t(v$Ճׅu tC4$Ћ[u$S@4a@[^_]ÍvS@uÍSD$ $tBD$ $S@D$$Ca@S@S@$S@C4a@1[ø'SS@\$ u1[Ð&$S@a@S@t9u N9t(‹Bu$S@4a@1[Ð&HJ$&$S@4a@ыBS@ڍt&SD$$ru S@[ÐS@S@uޡS@tX$ۉuS@S@$S@a@랉'S@tS@[Ív$S@0a@Yt&c@<8PEt1ffx ÍvD$f8MZt1f΍&'VST$ \$R<rBDt1ɐP 9wP9r (9u1[^ÍvUWVS|$0<$i1҃w f=@MZt [^_]Ð@Ft<@@@h\t1 (9t&D$|$$uރډ[^_]f1[^_]Ít&1f=@MZtVS@tJ<@\$ @@@rDt"1ɍP 9rP9r(9u1ɉ[^Ð[^É'1f=@MZtÐ@Ft<@@Ðt&V1f=@MZSL$ t [^Ð&@t<@@@ZDt1f@' tt(9u1[^fƉ[^É'1f=@MZtø@@EЉÉ'1f=@MZtfVS@dtJ<@\$ @@@rDt 1ɍP 9rP9r(9u1[^f@$[^t&WV1f=@MZS\$t [^_Í@t<@@@tyQTtJ 9rJ9r(9u1[^_@u t&HuP tׅp [@^_ÐQP=L$ r -=w) XYÐ%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%|a@%xa@%pa@%la@%`a@%\a@ffffffS@Ív'D$S@ÐU]P'@ p'@0'@@'@N@Dlibgcj-16.dll_Jv_RegisterClasses S@@P@@Unknown error_matherr(): %s in %s(%g, %g) (retval=%g) Argument domain error (DOMAIN)Argument singularity (SIGN)Overflow range error (OVERFLOW)The result is too small to be represented (UNDERFLOW)Total loss of significance (TLOSS)Partial loss of significance (PLOSS)l@@@@@@@@A@$A@Mingw-w64 runtime failure: Address %p has no image-section VirtualQuery failed for %d bytes at address %p VirtualProtect failed with code 0x%x Unknown pseudo relocation protocol version %d. Unknown pseudo relocation bit size %d. GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205<`ea`e\aaaab&bdFdPdXdbdldvddddddaaab&bdFdPdXdbdldvddddddDeleteCriticalSectionEnterCriticalSectionGetCurrentProcessGetCurrentProcessIdGetCurrentThreadIdGetLastErrorGetModuleHandleAEGetProcAddressdGetStartupInfoA{GetSystemTimeAsFileTimeGetTickCountInitializeCriticalSection&LeaveCriticalSectionQueryPerformanceCountergSetUnhandledExceptionFiltertSleepTerminateProcessTlsGetValueUnhandledExceptionFilterVirtualProtectVirtualQuery7__dllonexit:__getmainargs;__initenvD__lconv_inith__set_app_typek__setusermatherry_acmdln_amsg_exit_cexit_fmode0_initterm4_iob_lock2_onexitFcallocPexit`fprintfgfreerfwritemallocmemcpysignalstrlenstrncmp_unlock;abortWvfprintf`````````````````````KERNEL32.dll```````````````````````````msvcrt.dll0@@@@p@@@S@ p@yara-4.1.3/tests/data/tiny-idata-51ff000066400000000000000000001000001413423160300172370ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PELVV  p0@ܨ `a.textt `P`.data000@0.rdataP@@@0@.bssP`.idata`Q@0.CRT4p`@0.tls p@0Í&'1f=@MZS@S@S@$P@thP@S@tJ$$ S@S@S@a@=0@tm1Í&$`f<@@PE@uQf t?f j]1Kv$@1Ãyt,1f,S@D$P@D$P@D$P@$P@P@ P@D$ ,fU1WVUS׃|0)čD$@@@ @@@@̃5S@d1X=@a@9$׃S@uޡS@1ۃS@MP@S@,@@tD$D$$Ѓ b $@ vU,0@t<$@@a@tD$@@$ a@…t $,0@$`@Í&U]ÐUu*ÐffffffS(S@$D$$*S@$YD$S@$HD$D$D$D$D$D$0$$ËD$$*S@D$$$S@d([ÐD$0$a@([Í&'D$ $1Ð0@t fС0@P@0@u Ít&S`'@t!t `'@u$p@[1ÍC`'@uɍv'P@tÐt&P@딐%ha@1ÐUWVS,$0@D$D$=N@tУ(0@,[^_]ÍD$$(a@\$3\$a@a@,a@ƍD$$8a@3\$3\$111N@tЉ5$0@(0@,[^_]ÐDO@t&U( S@ EU$S@$Q@P@,S@EP@$0@E(0@EH)ЉẺiËŋE)ljẺN>뮋 S@AD$A@$A@D$D$$A@US]=wK==D$$ t-$ c'=t)==tWS@t=]]t&D$$pt$и]v'D$$0uD$$~=jD$$tXI$|fD$$a&D$$ A&D$$!UWVS$S@a@S@-Ha@=a@t(v$Ճׅu tC4$Ћ[u$S@4a@[^_]ÍvS@uÍSD$ $tBD$ $S@D$$Ca@S@S@$S@C4a@1[ø'SS@\$ u1[Ð&$S@a@S@t9u N9t(‹Bu$S@4a@1[Ð&HJ$&$S@4a@ыBS@ڍt&SD$$ru S@[ÐS@S@uޡS@tX$ۉuS@S@$S@a@랉'S@tS@[Ív$S@0a@Yt&c@<8PEt1ffx ÍvD$f8MZt1f΍&'VST$ \$R<rBDt1ɐP 9wP9r (9u1[^ÍvUWVS|$0<$i1҃w f=@MZt [^_]Ð@Ft<@@@h\t1 (9t&D$|$$uރډ[^_]f1[^_]Ít&1f=@MZtVS@tJ<@\$ @@@rDt"1ɍP 9rP9r(9u1ɉ[^Ð[^É'1f=@MZtÐ@Ft<@@Ðt&V1f=@MZSL$ t [^Ð&@t<@@@ZDt1f@' tt(9u1[^fƉ[^É'1f=@MZtø@@EЉÉ'1f=@MZtfVS@dtJ<@\$ @@@rDt 1ɍP 9rP9r(9u1[^f@$[^t&WV1f=@MZS\$t [^_Í@t<@@@tyQTtJ 9rJ9r(9u1[^_@u t&HuP tׅp [@^_ÐQP=L$ r -=w) XYÐ%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%|a@%xa@%pa@%la@%`a@%\a@ffffffS@Ív'D$S@ÐU]P'@ p'@0'@@'@N@Dlibgcj-16.dll_Jv_RegisterClasses S@@P@@Unknown error_matherr(): %s in %s(%g, %g) (retval=%g) Argument domain error (DOMAIN)Argument singularity (SIGN)Overflow range error (OVERFLOW)The result is too small to be represented (UNDERFLOW)Total loss of significance (TLOSS)Partial loss of significance (PLOSS)l@@@@@@@@A@$A@Mingw-w64 runtime failure: Address %p has no image-section VirtualQuery failed for %d bytes at address %p VirtualProtect failed with code 0x%x Unknown pseudo relocation protocol version %d. Unknown pseudo relocation bit size %d. GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205<`ea`e\aaaab&bdFdPdXdbdldvddddddaaab&bdFdPdXdbdldvddddddDeleteCriticalSectionEnterCriticalSectionGetCurrentProcessGetCurrentProcessIdGetCurrentThreadIdGetLastErrorGetModuleHandleAEGetProcAddressdGetStartupInfoA{GetSystemTimeAsFileTimeGetTickCountInitializeCriticalSection&LeaveCriticalSectionQueryPerformanceCountergSetUnhandledExceptionFiltertSleepTerminateProcessTlsGetValueUnhandledExceptionFilterVirtualProtectVirtualQuery7__dllonexit:__getmainargs;__initenvD__lconv_inith__set_app_typek__setusermatherry_acmdln_amsg_exit_cexit_fmode0_initterm4_iob_lock2_onexitFcallocPexit`fprintfgfreerfwritemallocmemcpysignalstrlenstrncmp_unlock;abortWvfprintf`````````````````````KERNEL32.dll```````````````````````````msvcrt.dll0@@@@p@@@S@ p@yara-4.1.3/tests/data/tiny-idata-5200000066400000000000000000001000001413423160300170640ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PELVV  p0@ܨ `a.textt `P`.data000@0.rdataP@@@0@.bssP`.idata`R@0.CRT4p`@0.tls p@0Í&'1f=@MZS@S@S@$P@thP@S@tJ$$ S@S@S@a@=0@tm1Í&$`f<@@PE@uQf t?f j]1Kv$@1Ãyt,1f,S@D$P@D$P@D$P@$P@P@ P@D$ ,fU1WVUS׃|0)čD$@@@ @@@@̃5S@d1X=@a@9$׃S@uޡS@1ۃS@MP@S@,@@tD$D$$Ѓ b $@ vU,0@t<$@@a@tD$@@$ a@…t $,0@$`@Í&U]ÐUu*ÐffffffS(S@$D$$*S@$YD$S@$HD$D$D$D$D$D$0$$ËD$$*S@D$$$S@d([ÐD$0$a@([Í&'D$ $1Ð0@t fС0@P@0@u Ít&S`'@t!t `'@u$p@[1ÍC`'@uɍv'P@tÐt&P@딐%ha@1ÐUWVS,$0@D$D$=N@tУ(0@,[^_]ÍD$$(a@\$3\$a@a@,a@ƍD$$8a@3\$3\$111N@tЉ5$0@(0@,[^_]ÐDO@t&U( S@ EU$S@$Q@P@,S@EP@$0@E(0@EH)ЉẺiËŋE)ljẺN>뮋 S@AD$A@$A@D$D$$A@US]=wK==D$$ t-$ c'=t)==tWS@t=]]t&D$$pt$и]v'D$$0uD$$~=jD$$tXI$|fD$$a&D$$ A&D$$!UWVS$S@a@S@-Ha@=a@t(v$Ճׅu tC4$Ћ[u$S@4a@[^_]ÍvS@uÍSD$ $tBD$ $S@D$$Ca@S@S@$S@C4a@1[ø'SS@\$ u1[Ð&$S@a@S@t9u N9t(‹Bu$S@4a@1[Ð&HJ$&$S@4a@ыBS@ڍt&SD$$ru S@[ÐS@S@uޡS@tX$ۉuS@S@$S@a@랉'S@tS@[Ív$S@0a@Yt&c@<8PEt1ffx ÍvD$f8MZt1f΍&'VST$ \$R<rBDt1ɐP 9wP9r (9u1[^ÍvUWVS|$0<$i1҃w f=@MZt [^_]Ð@Ft<@@@h\t1 (9t&D$|$$uރډ[^_]f1[^_]Ít&1f=@MZtVS@tJ<@\$ @@@rDt"1ɍP 9rP9r(9u1ɉ[^Ð[^É'1f=@MZtÐ@Ft<@@Ðt&V1f=@MZSL$ t [^Ð&@t<@@@ZDt1f@' tt(9u1[^fƉ[^É'1f=@MZtø@@EЉÉ'1f=@MZtfVS@dtJ<@\$ @@@rDt 1ɍP 9rP9r(9u1[^f@$[^t&WV1f=@MZS\$t [^_Í@t<@@@tyQTtJ 9rJ9r(9u1[^_@u t&HuP tׅp [@^_ÐQP=L$ r -=w) XYÐ%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%|a@%xa@%pa@%la@%`a@%\a@ffffffS@Ív'D$S@ÐU]P'@ p'@0'@@'@N@Dlibgcj-16.dll_Jv_RegisterClasses S@@P@@Unknown error_matherr(): %s in %s(%g, %g) (retval=%g) Argument domain error (DOMAIN)Argument singularity (SIGN)Overflow range error (OVERFLOW)The result is too small to be represented (UNDERFLOW)Total loss of significance (TLOSS)Partial loss of significance (PLOSS)l@@@@@@@@A@$A@Mingw-w64 runtime failure: Address %p has no image-section VirtualQuery failed for %d bytes at address %p VirtualProtect failed with code 0x%x Unknown pseudo relocation protocol version %d. Unknown pseudo relocation bit size %d. GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205<`ea`e\aaaab&bdFdPdXdbdldvddddddaaab&bdFdPdXdbdldvddddddDeleteCriticalSectionEnterCriticalSectionGetCurrentProcessGetCurrentProcessIdGetCurrentThreadIdGetLastErrorGetModuleHandleAEGetProcAddressdGetStartupInfoA{GetSystemTimeAsFileTimeGetTickCountInitializeCriticalSection&LeaveCriticalSectionQueryPerformanceCountergSetUnhandledExceptionFiltertSleepTerminateProcessTlsGetValueUnhandledExceptionFilterVirtualProtectVirtualQuery7__dllonexit:__getmainargs;__initenvD__lconv_inith__set_app_typek__setusermatherry_acmdln_amsg_exit_cexit_fmode0_initterm4_iob_lock2_onexitFcallocPexit`fprintfgfreerfwritemallocmemcpysignalstrlenstrncmp_unlock;abortWvfprintf`````````````````````KERNEL32.dll```````````````````````````msvcrt.dll0@@@@p@@@S@ p@yara-4.1.3/tests/data/tiny-macho000066400000000000000000000100001413423160300165050ustar00rootroot00000000000000 PHi Frand̀jP̀t8SP1jWjPyara-4.1.3/tests/data/tiny-overlay000066400000000000000000001000071413423160300171060ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PELVV  p0@ܨ `a.textt `P`.data000@0.rdataP@@@0@.bssP`.idata`P@0.CRT4p`@0.tls p@0Í&'1f=@MZS@S@S@$P@thP@S@tJ$$ S@S@S@a@=0@tm1Í&$`f<@@PE@uQf t?f j]1Kv$@1Ãyt,1f,S@D$P@D$P@D$P@$P@P@ P@D$ ,fU1WVUS׃|0)čD$@@@ @@@@̃5S@d1X=@a@9$׃S@uޡS@1ۃS@MP@S@,@@tD$D$$Ѓ b $@ vU,0@t<$@@a@tD$@@$ a@…t $,0@$`@Í&U]ÐUu*ÐffffffS(S@$D$$*S@$YD$S@$HD$D$D$D$D$D$0$$ËD$$*S@D$$$S@d([ÐD$0$a@([Í&'D$ $1Ð0@t fС0@P@0@u Ít&S`'@t!t `'@u$p@[1ÍC`'@uɍv'P@tÐt&P@딐%ha@1ÐUWVS,$0@D$D$=N@tУ(0@,[^_]ÍD$$(a@\$3\$a@a@,a@ƍD$$8a@3\$3\$111N@tЉ5$0@(0@,[^_]ÐDO@t&U( S@ EU$S@$Q@P@,S@EP@$0@E(0@EH)ЉẺiËŋE)ljẺN>뮋 S@AD$A@$A@D$D$$A@US]=wK==D$$ t-$ c'=t)==tWS@t=]]t&D$$pt$и]v'D$$0uD$$~=jD$$tXI$|fD$$a&D$$ A&D$$!UWVS$S@a@S@-Ha@=a@t(v$Ճׅu tC4$Ћ[u$S@4a@[^_]ÍvS@uÍSD$ $tBD$ $S@D$$Ca@S@S@$S@C4a@1[ø'SS@\$ u1[Ð&$S@a@S@t9u N9t(‹Bu$S@4a@1[Ð&HJ$&$S@4a@ыBS@ڍt&SD$$ru S@[ÐS@S@uޡS@tX$ۉuS@S@$S@a@랉'S@tS@[Ív$S@0a@Yt&c@<8PEt1ffx ÍvD$f8MZt1f΍&'VST$ \$R<rBDt1ɐP 9wP9r (9u1[^ÍvUWVS|$0<$i1҃w f=@MZt [^_]Ð@Ft<@@@h\t1 (9t&D$|$$uރډ[^_]f1[^_]Ít&1f=@MZtVS@tJ<@\$ @@@rDt"1ɍP 9rP9r(9u1ɉ[^Ð[^É'1f=@MZtÐ@Ft<@@Ðt&V1f=@MZSL$ t [^Ð&@t<@@@ZDt1f@' tt(9u1[^fƉ[^É'1f=@MZtø@@EЉÉ'1f=@MZtfVS@dtJ<@\$ @@@rDt 1ɍP 9rP9r(9u1[^f@$[^t&WV1f=@MZS\$t [^_Í@t<@@@tyQTtJ 9rJ9r(9u1[^_@u t&HuP tׅp [@^_ÐQP=L$ r -=w) XYÐ%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%a@%|a@%xa@%pa@%la@%`a@%\a@ffffffS@Ív'D$S@ÐU]P'@ p'@0'@@'@N@Dlibgcj-16.dll_Jv_RegisterClasses S@@P@@Unknown error_matherr(): %s in %s(%g, %g) (retval=%g) Argument domain error (DOMAIN)Argument singularity (SIGN)Overflow range error (OVERFLOW)The result is too small to be represented (UNDERFLOW)Total loss of significance (TLOSS)Partial loss of significance (PLOSS)l@@@@@@@@A@$A@Mingw-w64 runtime failure: Address %p has no image-section VirtualQuery failed for %d bytes at address %p VirtualProtect failed with code 0x%x Unknown pseudo relocation protocol version %d. Unknown pseudo relocation bit size %d. GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205GCC: (GNU) 5.3.1 20160205<`ea`e\aaaab&bdFdPdXdbdldvddddddaaab&bdFdPdXdbdldvddddddDeleteCriticalSectionEnterCriticalSectionGetCurrentProcessGetCurrentProcessIdGetCurrentThreadIdGetLastErrorGetModuleHandleAEGetProcAddressdGetStartupInfoA{GetSystemTimeAsFileTimeGetTickCountInitializeCriticalSection&LeaveCriticalSectionQueryPerformanceCountergSetUnhandledExceptionFiltertSleepTerminateProcessTlsGetValueUnhandledExceptionFilterVirtualProtectVirtualQuery7__dllonexit:__getmainargs;__initenvD__lconv_inith__set_app_typek__setusermatherry_acmdln_amsg_exit_cexit_fmode0_initterm4_iob_lock2_onexitFcallocPexit`fprintfgfreerfwritemallocmemcpysignalstrlenstrncmp_unlock;abortWvfprintf`````````````````````KERNEL32.dll```````````````````````````msvcrt.dll0@@@@p@@@S@ p@overlayyara-4.1.3/tests/data/tiny-universal000066400000000000000000000605401413423160300174440ustar00rootroot00000000000000!@ @!` $ 8__PAGEZERO__TEXT__text__TEXT__symbol_stub__TEXTf f__stub_helper__TEXTt t__cstring__TEXT__unwind_info__TEXTH__DATA __nl_symbol_ptr__DATA __la_symbol_ptr__DATA 8__LINKEDIT0 @"0  ( D <  H P  /usr/lib/dyldCU];]Ⱥ$ *( 4/usr/lib/libSystem.B.dylib& ) + UEE} E EM $EMȉME]UV4XM UEUMEE䍈U $T$Q=5E$_M䍑EEu$D$t$E4^]% % h % hh%dfactorial( %d ) = %d 44g4 "R!pppQ@dyld_stub_binderQrr@_printfr @_scanf__mh_execute_header,factorial0main5P !'/6@ __mh_execute_header_factorial_main_printf_scanfdyld_stub_binder H__PAGEZERO(__TEXT__text__TEXT__stubs__TEXTF F__stub_helper__TEXTT$T__cstring__TEXTxx__unwind_info__TEXTH__eh_frame__TEXT__DATA__nl_symbol_ptr__DATA__la_symbol_ptr__DATAH__LINKEDIT  `"0   @ @ !H P! /usr/lib/dyldW5-92$ *( 8/usr/lib/libSystem.B.dylib& ) + UHH}} EEMωEMȉMEH]@UHH E}HuH={HuD='}uH=YEuUEH ]Ð%%LAS%hh%dfactorial( %d ) = %d 44F4 zRx dn"R@dyld_stub_binderQrr@_printfr@_scanf__mh_execute_header,factorial0main5P !'/6@ __mh_execute_header_factorial_main_printf_scanfdyld_stub_binderyara-4.1.3/tests/data/tiny.notes000066400000000000000000000014441413423160300165630ustar00rootroot00000000000000tiny.exe was compiled from a simple oneliner, int main() { return 42; } $ i686-w64-mingw32-gcc -s -Wl,--file-alignment=4096 -o tiny.exe tiny.c To demonstrate issue #429, two patched executables have been generated where the PointerToRawData for the .idata section (offset 0x22c) was changed from 0x5000 to 0x51ff (tiny-idata-51ff.exe) and 0x5200 (tiny-idata-5200.exe), respectively. While tiny-idata-51ff.exe can be executed in Windows XP, tiny-idata-5200.exe can not. Compiler version used to produce tiny.exe: $ i686-w64-mingw32-gcc --version i686-w64-mingw32-gcc (GCC) 5.3.1 20160205 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. yara-4.1.3/tests/data/weird_rich000066400000000000000000000015101413423160300165620ustar00rootroot00000000000000MZ@P !L!This program cannot be run in DOS mode. $UsҊ@RichPELM  "("!PHyara-4.1.3/tests/data/x.txt000066400000000000000000036412431413423160300155500ustar00rootroot00000000000000XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXyara-4.1.3/tests/data/xor.out000066400000000000000000000143571413423160300160760ustar00rootroot000000000000000x0 This program cannot 0x1 Uihr!qsnfs`l!b`oonu 0x2 Vjkq"rpmepco"acllmv 0x3 Wkjp#sqldqbn#`bmmlw 0x4 Plmw$tvkcvei$gejjkp 0x5 Qmlv%uwjbwdh%fdkkjq 0x6 Rnou&vtiatgk&eghhir 0x7 Sont'wuh`ufj'dfiihs 0x8 \`a{(xzgozie(kiffg| 0x9 ]a`z)y{fn{hd)jhggf} 0xa ^bcy*zxemxkg*ikdde~ 0xb _cbx+{ydlyjf+hjeed 0xc Xde,|~ck~ma,ombbcx 0xd Yed~-}bjl`-nlccby 0xe Zfg}.~|ai|oc.mo``az 0xf [gf|/}`h}nb/lnaa`{ 0x10 Dxyc0`bwbq}0sq~~d 0x11 Eyxb1ac~vcp|1rp~e 0x12 Fz{a2b`}u`s2qs||}f 0x13 G{z`3ca|tar~3pr}}|g 0x14 @|}g4df{sfuy4wuzz{` 0x15 A}|f5egzrgtx5vt{{za 0x16 B~e6fdyqdw{6uwxxyb 0x17 C~d7gexpevz7tvyyxc 0x18 Lpqk8hjwjyu8{yvvwl 0x19 Mqpj9ikv~kxt9zxwwvm 0x1a Nrsi:jhu}h{w:y{ttun 0x1b Osrh;kit|izv;xzuuto 0x1c Htuonlqyls>}ppqj 0x1f Kwvl?ompxm~r?|~qqpk 0x20 tHISPROGRAMCANNOT 0x21 uIHRQSNFS@LB@OONU 0x22 vJKQRPMEPCOACLLMV 0x23 wKJPSQLDQBN@BMMLW 0x24 pLMWTVKCVEIGEJJKP 0x25 qMLVUWJBWDHFDKKJQ 0x26 rNOUVTIATGKEGHHIR 0x27 sONTWUH@UFJDFIIHS 0x28 |@A[XZGOZIEKIFFG\ 0x29 }A@Z Y[FN[HD JHGGF] 0x2a ~BCY ZXEMXKG IKDDE^ 0x2b CBX [YDLYJF HJEED_ 0x2c xDE_ \^CK^MA OMBBCX 0x2d yED^ ]_BJ_L@ NLCCBY 0x2e zFG]^\AI\OCMO@@AZ 0x2f {GF\_]@H]NBLNAA@[ 0x30 dXYC@B_WBQ]SQ^^_D 0x31 eYXBAC^VCP\RP__^E 0x32 fZ[AB@]U@S_QS\\]F 0x33 g[Z@CA\TAR^PR]]\G 0x34 `\]GDF[SFUYWUZZ[@ 0x35 a]\FEGZRGTXVT[[ZA 0x36 b^_EFDYQDW[UWXXYB 0x37 c_^DGEXPEVZTVYYXC 0x38 lPQKHJW_JYU[YVVWL 0x39 mQPJIKV^KXTZXWWVM 0x3a nRSIJHU]H[WY[TTUN 0x3b oSRHKIT\IZVXZUUTO 0x3c hTUOLNS[N]Q_]RRSH 0x3d iUTNMORZO\P^\SSRI 0x3e jVWMNLQYL_S]_PPQJ 0x3f kWVLOMPXM^R\^QQPK 0x40 ()3`02/'2!-`#!../4 0x41 )(2a13.&3 ,a" //.5 0x42 *+1b20-%0#/b!#,,-6 0x43 +*0c31,$1".c "--,7 0x44 ,-7d46+#6%)d'%**+0 0x45 -,6e57*"7$(e&$++*1 0x46 ./5f64)!4'+f%'(()2 0x47 /.4g75( 5&*g$&))(3 0x48  !;h8:'/:)%h+)&&'< 0x49 ! :i9;&.;($i*(''&= 0x4a "#9j:8%-8+'j)+$$%> 0x4b #"8k;9$,9*&k(*%%$? 0x4c $%?l<>#+>-!l/-""#8 0x4d %$>m=?"*?, m.,##"9 0x4e &'=n>>?$ 0x51 98"q!#>6#0% 0x52 :;!r" =5 3?r13<<=& 0x53 ;: s#!<4!2>s02==<' 0x54 <='t$&;3&59t75::; 0x55 =<&u%':2'48u64;;:! 0x56 >?%v&$91$7;v57889" 0x57 ?>$w'%80%6:w46998# 0x58 01+x(*7?*95x;9667, 0x59 10*y)+6>+84y:8776- 0x5a 23)z*(5=(;7z9;445. 0x5b 32({+)4<):6{8:554/ 0x5c 45/|,.3;.=1|?=223( 0x5d 54.}-/2:/<0}><332) 0x5e 67-~.,19,?3~=?001* 0x5f 76,/-08->2<>110+ 0x60 4 @ @ 0x61 5 A A 0x62 6 B B  0x63 7 C C  0x64 0 D  D  0x65 1 E E  0x66 2F  F  0x67 3G G  0x68 <H H  0x69 =II  0x6a >J  J  0x6b ?K  K  0x6c 8L  L  0x6d 9M  M  0x6e :N N  0x6f ;OO  0x70 $PP 0x71 %QQ 0x72 &RR 0x73 'SS 0x74 TT 0x75 !UU 0x76 "VV 0x77 #WW 0x78 , X  X 0x79 - Y  Y 0x7a . Z Z 0x7b /[  [ 0x7c (\ \ 0x7d )] ] 0x7e * ^  ^ 0x7f + _  _ 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f 0xa0 Ӏ̀ 0xa1 ҁ́ 0xa2 тς 0xa3 Ѓ΃ 0xa4 ׄɄ 0xa5 օȅ 0xa6 Նˆ 0xa7 ԇʇ 0xa8 ۈň 0xa9 ډĉ 0xaa يNJ 0xab ؋Ƌ 0xac ߌ 0xad ލ 0xae ݎÎ 0xaf ܏ 0xb0 Ðݐ 0xb1 ‘ܑ 0xb2 ߒ 0xb3 ޓ 0xb4 ǔٔ 0xb5 ƕؕ 0xb6 Ŗۖ 0xb7 ėڗ 0xb8 ˘՘ 0xb9 ʙԙ 0xba ɚך 0xbb ț֛ 0xbc Ϝќ 0xbd ΝН 0xbe ͞Ӟ 0xbf ̟ҟ 0xc0 ల࣡ 0xc1 ᱳᢠ 0xc2 Ⲱ⡣ 0xc3 㳱㠢 0xc4 䴶䧥 0xc5 嵷妤 0xc6 涴楧 0xc7 緵礦 0xc8 踺諩 0xc9 鹻骨 0xca 꺸ꩫ 0xcb 뻹먪 0xcc 켾쯭 0xcd 0xce  0xcf �אַ 0xd0 𠢿𳱾 0xd1 񡣾񲰿 0xd2 򢠽򱳼 0xd3 󣡼󰲽 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf 0xe0 0xe1 0xe2 ’ 0xe3 ÓÀ 0xe4 Ĕć 0xe5 ŕņ 0xe6 Ɩƅ 0xe7 ǗDŽ 0xe8 Șȋ 0xe9 əɊ 0xea ʚʉ 0xeb ˛ˈ 0xec ̜̏ 0xed ͎͝ 0xee Ξ΍ 0xef ϟό 0xf0 ЀГ 0xf1 сђ 0xf2 ҂ґ 0xf3 ӃӐ 0xf4 Ԅԗ 0xf5 ՅՖ 0xf6 ֆ֕ 0xf7 ׇה 0xf8 ؈؛ 0xf9 ىٚ 0xfa ڊڙ 0xfb ۋۘ 0xfc ܌ܟ 0xfd ݍݞ 0xfe ގޝ 0xff ߏߜyara-4.1.3/tests/data/xorwide.out000066400000000000000000000257571413423160300167550ustar00rootroot000000000000000x0 This program cannot 0x1 Uihr!qsnfs`l!b`oonu 0x2 Vjkq"rpmepco"acllmv 0x3 Wkjp#sqldqbn#`bmmlw 0x4 Plmw$tvkcvei$gejjkp 0x5 Qmlv%uwjbwdh%fdkkjq 0x6 Rnou&vtiatgk&eghhir 0x7 Sont'wuh`ufj'dfiihs 0x8 \`a{(xzgozie(kiffg| 0x9 ] a ` z ) y { f n { h d ) j h g g f } 0xa ^ b c y * z x e m x k g * i k d d e ~ 0xb _ c b x + { y d l y j f + h j e e d  0xc X d e  , | ~ c k ~ m a , o m b b c x 0xd Y e d ~ - }  b j  l ` - n l c c b y 0xe Zfg}.~|ai|oc.mo``az 0xf [gf|/}`h}nb/lnaa`{ 0x10 Dxyc0`bwbq}0sq~~d 0x11 Eyxb1ac~vcp|1rp~e 0x12 Fz{a2b`}u`s2qs||}f 0x13 G{z`3ca|tar~3pr}}|g 0x14 @|}g4df{sfuy4wuzz{` 0x15 A}|f5egzrgtx5vt{{za 0x16 B~e6fdyqdw{6uwxxyb 0x17 C~d7gexpevz7tvyyxc 0x18 Lpqk8hjwjyu8{yvvwl 0x19 Mqpj9ikv~kxt9zxwwvm 0x1a Nrsi:jhu}h{w:y{ttun 0x1b Osrh;kit|izv;xzuuto 0x1c Htuo<lns{n}q<}rrsh 0x1d Iutn=morzo|p=~|ssri 0x1e Jvwm>nlqyls>}ppqj 0x1f Kwvl?ompxm~r?|~qqpk 0x20 t H I S P R O G R A M C A N N O T 0x21 u!I!H!R!!Q!S!N!F!S!@!L!!B!@!O!O!N!U! 0x22 v"J"K"Q""R"P"M"E"P"C"O""A"C"L"L"M"V" 0x23 w#K#J#P##S#Q#L#D#Q#B#N##@#B#M#M#L#W# 0x24 p$L$M$W$$T$V$K$C$V$E$I$$G$E$J$J$K$P$ 0x25 q%M%L%V%%U%W%J%B%W%D%H%%F%D%K%K%J%Q% 0x26 r&N&O&U&&V&T&I&A&T&G&K&&E&G&H&H&I&R& 0x27 s'O'N'T''W'U'H'@'U'F'J''D'F'I'I'H'S' 0x28 |(@(A([((X(Z(G(O(Z(I(E((K(I(F(F(G(\( 0x29 })A)@)Z) )Y)[)F)N)[)H)D) )J)H)G)G)F)]) 0x2a ~*B*C*Y* *Z*X*E*M*X*K*G* *I*K*D*D*E*^* 0x2b +C+B+X+ +[+Y+D+L+Y+J+F+ +H+J+E+E+D+_+ 0x2c x,D,E,_, ,\,^,C,K,^,M,A, ,O,M,B,B,C,X, 0x2d y-E-D-^- -]-_-B-J-_-L-@- -N-L-C-C-B-Y- 0x2e z.F.G.]..^.\.A.I.\.O.C..M.O.@.@.A.Z. 0x2f {/G/F/\//_/]/@/H/]/N/B//L/N/A/A/@/[/ 0x30 d0X0Y0C00@0B0_0W0B0Q0]00S0Q0^0^0_0D0 0x31 e1Y1X1B11A1C1^1V1C1P1\11R1P1_1_1^1E1 0x32 f2Z2[2A22B2@2]2U2@2S2_22Q2S2\2\2]2F2 0x33 g3[3Z3@33C3A3\3T3A3R3^33P3R3]3]3\3G3 0x34 `4\4]4G44D4F4[4S4F4U4Y44W4U4Z4Z4[4@4 0x35 a5]5\5F55E5G5Z5R5G5T5X55V5T5[5[5Z5A5 0x36 b6^6_6E66F6D6Y6Q6D6W6[66U6W6X6X6Y6B6 0x37 c7_7^7D77G7E7X7P7E7V7Z77T7V7Y7Y7X7C7 0x38 l8P8Q8K88H8J8W8_8J8Y8U88[8Y8V8V8W8L8 0x39 m9Q9P9J99I9K9V9^9K9X9T99Z9X9W9W9V9M9 0x3a n:R:S:I::J:H:U:]:H:[:W::Y:[:T:T:U:N: 0x3b o;S;R;H;;K;I;T;\;I;Z;V;;X;Z;U;U;T;O; 0x3c hV>W>M>>N>L>Q>Y>L>_>S>>]>_>P>P>Q>J> 0x3f k?W?V?L??O?M?P?X?M?^?R??\?^?Q?Q?P?K? 0x40 @(@)@3@`@0@2@/@'@2@!@-@`@#@!@.@.@/@4@ 0x41 A)A(A2AaA1A3A.A&A3A A,AaA"A A/A/A.A5A 0x42 B*B+B1BbB2B0B-B%B0B#B/BbB!B#B,B,B-B6B 0x43 C+C*C0CcC3C1C,C$C1C"C.CcC C"C-C-C,C7C 0x44 D,D-D7DdD4D6D+D#D6D%D)DdD'D%D*D*D+D0D 0x45 E-E,E6EeE5E7E*E"E7E$E(EeE&E$E+E+E*E1E 0x46 F.F/F5FfF6F4F)F!F4F'F+FfF%F'F(F(F)F2F 0x47 G/G.G4GgG7G5G(G G5G&G*GgG$G&G)G)G(G3G 0x48 H H!H;HhH8H:H'H/H:H)H%HhH+H)H&H&H'HJ 0x4b K#K"K8KkK;K9K$K,K9K*K&KkK(K*K%K%K$K?K 0x4c L$L%L?LlLL#L+L>L-L!LlL/L-L"L"L#L8L 0x4d M%M$M>MmM=M?M"M*M?M,M MmM.M,M#M#M"M9M 0x4e N&N'N=NnN>NP>P?P$P 0x51 Q9Q8Q"QqQ!Q#Q>Q6Q#Q0QQ%Q 0x52 R:R;R!RrR"R R=R5R R3R?RrR1R3RSsS0S2S=S=SV?V%VvV&V$V9V1V$V7V;VvV5V7V8V8V9V"V 0x57 W?W>W$WwW'W%W8W0W%W6W:WwW4W6W9W9W8W#W 0x58 X0X1X+XxX(X*X7X?X*X9X5XxX;X9X6X6X7X,X 0x59 Y1Y0Y*YyY)Y+Y6Y>Y+Y8Y4YyY:Y8Y7Y7Y6Y-Y 0x5a Z2Z3Z)ZzZ*Z(Z5Z=Z(Z;Z7ZzZ9Z;Z4Z4Z5Z.Z 0x5b [3[2[([{[+[)[4[<[)[:[6[{[8[:[5[5[4[/[ 0x5c \4\5\/\|\,\.\3\;\.\=\1\|\?\=\2\2\3\(\ 0x5d ]5]4].]}]-]/]2]:]/]<]0]}]>]<]3]3]2])] 0x5e ^6^7^-^~^.^,^1^9^,^?^3^~^=^?^0^0^1^*^ 0x5f _7_6_,__/_-_0_8_-_>_2__<_>_1_1_0_+_ 0x60 4`` ``@``````` `@``````` 0x61 5a aaaAaaaaaaa aAaaaaaaa 0x62 6b b bbBbbb bbbbbBbbb b b bb 0x63 7c c ccCccc cccccCccc c c cc 0x64 0d d ddDddd dddd dDddd d d dd 0x65 1e e eeEeee eeeeeEeee e e ee 0x66 2ffffFfff ffff fFfffff ff 0x67 3ggggGggggggg gGggg g ggg 0x68 jjjjJjjjj jj jjJj j jjjjj 0x6b ?kkkkKkkkk kk kkKkk kkkkk 0x6c 8llllLllll ll llLll lllll 0x6d 9mmmmMmmmm mm mmMmm mmmmm 0x6e :nnnnNnnnn nnnnNn nnnnnn 0x6f ;ooooOooooooooOo oooooo 0x70 $ppppPppppppppPppppppp 0x71 %qqqqQqqqqqqqqQqqqqqqq 0x72 &rrrrRrrrrrrrrRrrrrrrr 0x73 'ssssSssssssssSsssssss 0x74 ttttTttttttttTttttttt 0x75 !uuuuUuuuuuuuuUuuuuuuu 0x76 "vvvvVvvvvvvvvVvvvvvvv 0x77 #wwwwWwwwwwwwwWwwwwwww 0x78 ,xxx xXxx xxx xxxXxxxxxx x 0x79 -yyy yYy y yyy yyyYyyyyyy y 0x7a .zzz zZz zzzzzzzZzzzzzzz 0x7b /{{{{[{ { {{{ {{{[{{{{{{{ 0x7c (||||\| |||||||\||||||| 0x7d )}}}}]} }}}}}}}]}}}}}} } 0x7e *~~~ ~^~~ ~~~ ~~~^~~~~~~ ~ 0x7f + _  _  0x80 Ԁ󀠀퀠 0x81 Ձ򁡁쁡 0x82 ւ񂢂 0x83 ׃ 0x84 Є鄤 0x85 х腥 0x86 ҆놦 0x87 Ӈ􇧇ꇧ 0x88 ܈刨 0x89 ݉䉩 0x8a ފ犪 0x8b ߋ拫 0x8c ،ጬ 0x8d ٍ 0x8e ڎ㎮ 0x8f ۏ⏯ 0x90 Đ㐰 0x91 ő⑱ 0x92 ƒᒲ 0x93 Ǔ 0x94 甴 0x95 敵 0x96 –営 0x97 ×䗷 0x98 ̘똸 0x99 ͙ꙹ 0x9a Κ隺 0x9b ϛ蛻 0x9c Ȝ񜼜 0x9d ɝ𝽝 0x9e ʞힾ󞾞 0x9f ˟쟿򟿟 0xa0 ȠɠӠРҠϠǠҠ͠àΠΠϠԠ 0xa1 ɡȡҡѡӡΡơӡ̡¡ϡϡΡա 0xa2 ʢˢѢҢТ͢ŢТâϢâ̢̢֢͢ 0xa3 ˣʣУӣѣ̣ģѣ£Σ£̣ͣͣף 0xa4 ̤ͤפԤ֤ˤä֤ŤɤǤŤʤʤˤФ 0xa5 ̥֥ͥեץʥ¥ץĥȥƥĥ˥˥ʥѥ 0xa6 ΦϦզ֦ԦɦԦǦ˦ŦǦȦȦɦҦ 0xa7 ϧΧԧקէȧէƧʧħƧɧɧȧӧ 0xa8 ۨبڨǨϨڨɨŨ˨ɨƨƨǨܨ 0xa9 ک٩۩ƩΩ۩ȩĩʩȩǩǩƩݩ 0xaa ªê٪ڪتŪͪت˪Ǫɪ˪ĪĪŪު 0xab ë«ث۫٫ī̫٫ʫƫȫʫūūī߫ 0xac ĬŬ߬ܬެìˬެͬϬͬ¬¬ìج 0xad ŭĭޭݭ߭­ʭ̭߭έ̭íí­٭ 0xae ƮǮݮޮܮɮܮϮîͮϮڮ 0xaf ǯƯܯ߯ݯȯݯί¯̯ίۯ 0xb0 ذٰð°߰װ°ѰݰӰѰްް߰İ 0xb1 ٱر±ñޱֱñбܱұб߱߱ޱű 0xb2 ڲ۲²ݲղӲ߲ѲӲܲܲݲƲ 0xb3 ۳ڳóܳԳҳ޳гҳݳݳܳdz 0xb4 ܴݴǴĴƴ۴Ӵƴմٴ״մڴڴ۴ 0xb5 ݵܵƵŵǵڵҵǵԵصֵԵ۵۵ڵ 0xb6 ޶߶ŶƶĶٶѶĶ׶۶ն׶ضضٶ¶ 0xb7 ߷޷ķǷŷطзŷַڷԷַٷٷط÷ 0xb8 иѸ˸ȸʸ׸߸ʸٸո۸ٸָָ׸̸ 0xb9 ѹйʹɹ˹ֹ޹˹عԹڹع׹׹ֹ͹ 0xba ҺӺɺʺȺպݺȺۺ׺ٺۺԺԺպκ 0xbb ӻһȻ˻ɻԻܻɻڻֻػڻջջԻϻ 0xbc Լռϼ̼μӼۼμݼѼ߼ݼҼҼӼȼ 0xbd սԽνͽϽҽڽϽܽн޽ܽӽӽҽɽ 0xbe ־׾;ξ̾Ѿپ̾߾Ӿݾ߾ооѾʾ 0xbf ׿ֿ̿ϿͿпؿͿ޿ҿܿ޿ѿѿп˿ 0xc0 0xc1 0xc2 ª«±²°­¥°£¯¡£¬¬­¶ 0xc3 ëêðóñìäñâîàâííì÷ 0xc4 ĬĭķĴĶīģĶĥĩħĥĪĪīİ 0xc5 ŭŬŶŵŷŪŢŷŤŨŦŤūūŪű 0xc6 ƮƯƵƶƴƩơƴƧƫƥƧƨƨƩƲ 0xc7 ǯǮǴǷǵǨǠǵǦǪǤǦǩǩǨdz 0xc8 ȠȡȻȸȺȧȯȺȩȥȫȩȦȦȧȼ 0xc9 ɡɠɺɹɻɦɮɻɨɤɪɨɧɧɦɽ 0xca ʢʣʹʺʸʥʭʸʫʧʩʫʤʤʥʾ 0xcb ˣˢ˸˻˹ˤˬ˹˪˦˨˪˥˥ˤ˿ 0xcc ̸̡̢̢̤̥̼̣̫̭̯̭̣̿̾̾ 0xcd ͥͤ;ͽͿͪ͢Ϳͬͮͬͣͣ͢͠͹ 0xce ΦΧνξμΡΩμίΣέίΠΠΡκ 0xcf ϧϦϼϿϽϠϨϽϮϢϬϮϡϡϠϻ 0xd0 ийУРТпзТбнгбоопФ 0xd1 ѹѸѢѡѣѾѶѣѰѼѲѰѿѿѾѥ 0xd2 ҺһҡҢҠҽҵҠҳҿұҳҼҼҽҦ 0xd3 ӻӺӠӣӡӼӴӡӲӾӰӲӽӽӼӧ 0xd4 ԼԽԧԤԦԻԳԦԵԹԷԵԺԺԻԠ 0xd5 սռզեէպղէմոնմջջպա 0xd6 ־ֱֵַַָָֹֹֻֿ֥֦֤֤֢ 0xd7 ׿׾פקץ׸װץ׶׺״׶׹׹׸ף 0xd8 ذرثبتطؿتعصػعضضطج 0xd9 ٱٰ٪٩٫ٶپ٫ٸٴٺٸٷٷٶ٭ 0xda ڲڳکڪڨڵڽڨڻڷڹڻڴڴڵڮ 0xdb ۳۲ۨ۫۩۴ۼ۩ۺ۶۸ۺ۵۵۴ۯ 0xdc ܴܵܯܬܮܻܳܮܱܽܿܽܲܲܳܨ 0xdd ݵݴݮݭݯݲݺݯݼݰݾݼݳݳݲݩ 0xde ޶޷ޭޮެޱ޹ެ޿޳޽޿ްްޱު 0xdf ߷߶߬߯߭߰߸߭߾߲߼߾߱߱߰߫ 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef 0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff yara-4.1.3/tests/data/xorwideandascii.out000066400000000000000000000423401413423160300204340ustar00rootroot000000000000000x0 This program cannot 0x1 Uihr!qsnfs`l!b`oonu 0x2 Vjkq"rpmepco"acllmv 0x3 Wkjp#sqldqbn#`bmmlw 0x4 Plmw$tvkcvei$gejjkp 0x5 Qmlv%uwjbwdh%fdkkjq 0x6 Rnou&vtiatgk&eghhir 0x7 Sont'wuh`ufj'dfiihs 0x8 \`a{(xzgozie(kiffg| 0x9 ]a`z)y{fn{hd)jhggf} 0xa ^bcy*zxemxkg*ikdde~ 0xb _cbx+{ydlyjf+hjeed 0xc Xde,|~ck~ma,ombbcx 0xd Yed~-}bjl`-nlccby 0xe Zfg}.~|ai|oc.mo``az 0xf [gf|/}`h}nb/lnaa`{ 0x10 Dxyc0`bwbq}0sq~~d 0x11 Eyxb1ac~vcp|1rp~e 0x12 Fz{a2b`}u`s2qs||}f 0x13 G{z`3ca|tar~3pr}}|g 0x14 @|}g4df{sfuy4wuzz{` 0x15 A}|f5egzrgtx5vt{{za 0x16 B~e6fdyqdw{6uwxxyb 0x17 C~d7gexpevz7tvyyxc 0x18 Lpqk8hjwjyu8{yvvwl 0x19 Mqpj9ikv~kxt9zxwwvm 0x1a Nrsi:jhu}h{w:y{ttun 0x1b Osrh;kit|izv;xzuuto 0x1c Htuonlqyls>}ppqj 0x1f Kwvl?ompxm~r?|~qqpk 0x20 tHISPROGRAMCANNOT 0x21 uIHRQSNFS@LB@OONU 0x22 vJKQRPMEPCOACLLMV 0x23 wKJPSQLDQBN@BMMLW 0x24 pLMWTVKCVEIGEJJKP 0x25 qMLVUWJBWDHFDKKJQ 0x26 rNOUVTIATGKEGHHIR 0x27 sONTWUH@UFJDFIIHS 0x28 |@A[XZGOZIEKIFFG\ 0x29 }A@Z Y[FN[HD JHGGF] 0x2a ~BCY ZXEMXKG IKDDE^ 0x2b CBX [YDLYJF HJEED_ 0x2c xDE_ \^CK^MA OMBBCX 0x2d yED^ ]_BJ_L@ NLCCBY 0x2e zFG]^\AI\OCMO@@AZ 0x2f {GF\_]@H]NBLNAA@[ 0x30 dXYC@B_WBQ]SQ^^_D 0x31 eYXBAC^VCP\RP__^E 0x32 fZ[AB@]U@S_QS\\]F 0x33 g[Z@CA\TAR^PR]]\G 0x34 `\]GDF[SFUYWUZZ[@ 0x35 a]\FEGZRGTXVT[[ZA 0x36 b^_EFDYQDW[UWXXYB 0x37 c_^DGEXPEVZTVYYXC 0x38 lPQKHJW_JYU[YVVWL 0x39 mQPJIKV^KXTZXWWVM 0x3a nRSIJHU]H[WY[TTUN 0x3b oSRHKIT\IZVXZUUTO 0x3c hTUOLNS[N]Q_]RRSH 0x3d iUTNMORZO\P^\SSRI 0x3e jVWMNLQYL_S]_PPQJ 0x3f kWVLOMPXM^R\^QQPK 0x40 ()3`02/'2!-`#!../4 0x41 )(2a13.&3 ,a" //.5 0x42 *+1b20-%0#/b!#,,-6 0x43 +*0c31,$1".c "--,7 0x44 ,-7d46+#6%)d'%**+0 0x45 -,6e57*"7$(e&$++*1 0x46 ./5f64)!4'+f%'(()2 0x47 /.4g75( 5&*g$&))(3 0x48  !;h8:'/:)%h+)&&'< 0x49 ! :i9;&.;($i*(''&= 0x4a "#9j:8%-8+'j)+$$%> 0x4b #"8k;9$,9*&k(*%%$? 0x4c $%?l<>#+>-!l/-""#8 0x4d %$>m=?"*?, m.,##"9 0x4e &'=n>>?$ 0x51 98"q!#>6#0% 0x52 :;!r" =5 3?r13<<=& 0x53 ;: s#!<4!2>s02==<' 0x54 <='t$&;3&59t75::; 0x55 =<&u%':2'48u64;;:! 0x56 >?%v&$91$7;v57889" 0x57 ?>$w'%80%6:w46998# 0x58 01+x(*7?*95x;9667, 0x59 10*y)+6>+84y:8776- 0x5a 23)z*(5=(;7z9;445. 0x5b 32({+)4<):6{8:554/ 0x5c 45/|,.3;.=1|?=223( 0x5d 54.}-/2:/<0}><332) 0x5e 67-~.,19,?3~=?001* 0x5f 76,/-08->2<>110+ 0x60 4 @ @ 0x61 5 A A 0x62 6 B B  0x63 7 C C  0x64 0 D  D  0x65 1 E E  0x66 2F  F  0x67 3G G  0x68 <H H  0x69 =II  0x6a >J  J  0x6b ?K  K  0x6c 8L  L  0x6d 9M  M  0x6e :N N  0x6f ;OO  0x70 $PP 0x71 %QQ 0x72 &RR 0x73 'SS 0x74 TT 0x75 !UU 0x76 "VV 0x77 #WW 0x78 , X  X 0x79 - Y  Y 0x7a . Z Z 0x7b /[  [ 0x7c (\ \ 0x7d )] ] 0x7e * ^  ^ 0x7f + _  _ 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f 0xa0 Ӏ̀ 0xa1 ҁ́ 0xa2 тς 0xa3 Ѓ΃ 0xa4 ׄɄ 0xa5 օȅ 0xa6 Նˆ 0xa7 ԇʇ 0xa8 ۈň 0xa9 ډĉ 0xaa يNJ 0xab ؋Ƌ 0xac ߌ 0xad ލ 0xae ݎÎ 0xaf ܏ 0xb0 Ðݐ 0xb1 ‘ܑ 0xb2 ߒ 0xb3 ޓ 0xb4 ǔٔ 0xb5 ƕؕ 0xb6 Ŗۖ 0xb7 ėڗ 0xb8 ˘՘ 0xb9 ʙԙ 0xba ɚך 0xbb ț֛ 0xbc Ϝќ 0xbd ΝН 0xbe ͞Ӟ 0xbf ̟ҟ 0xc0 ల࣡ 0xc1 ᱳᢠ 0xc2 Ⲱ⡣ 0xc3 㳱㠢 0xc4 䴶䧥 0xc5 嵷妤 0xc6 涴楧 0xc7 緵礦 0xc8 踺諩 0xc9 鹻骨 0xca 꺸ꩫ 0xcb 뻹먪 0xcc 켾쯭 0xcd 0xce  0xcf �אַ 0xd0 𠢿𳱾 0xd1 񡣾񲰿 0xd2 򢠽򱳼 0xd3 󣡼󰲽 0xd4 0xd5 0xd6 0xd7 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf 0xe0 0xe1 0xe2 ’ 0xe3 ÓÀ 0xe4 Ĕć 0xe5 ŕņ 0xe6 Ɩƅ 0xe7 ǗDŽ 0xe8 Șȋ 0xe9 əɊ 0xea ʚʉ 0xeb ˛ˈ 0xec ̜̏ 0xed ͎͝ 0xee Ξ΍ 0xef ϟό 0xf0 ЀГ 0xf1 сђ 0xf2 ҂ґ 0xf3 ӃӐ 0xf4 Ԅԗ 0xf5 ՅՖ 0xf6 ֆ֕ 0xf7 ׇה 0xf8 ؈؛ 0xf9 ىٚ 0xfa ڊڙ 0xfb ۋۘ 0xfc ܌ܟ 0xfd ݍݞ 0xfe ގޝ 0xff ߏߜ 0x0 This program cannot 0x1 Uihr!qsnfs`l!b`oonu 0x2 Vjkq"rpmepco"acllmv 0x3 Wkjp#sqldqbn#`bmmlw 0x4 Plmw$tvkcvei$gejjkp 0x5 Qmlv%uwjbwdh%fdkkjq 0x6 Rnou&vtiatgk&eghhir 0x7 Sont'wuh`ufj'dfiihs 0x8 \`a{(xzgozie(kiffg| 0x9 ] a ` z ) y { f n { h d ) j h g g f } 0xa ^ b c y * z x e m x k g * i k d d e ~ 0xb _ c b x + { y d l y j f + h j e e d  0xc X d e  , | ~ c k ~ m a , o m b b c x 0xd Y e d ~ - }  b j  l ` - n l c c b y 0xe Zfg}.~|ai|oc.mo``az 0xf [gf|/}`h}nb/lnaa`{ 0x10 Dxyc0`bwbq}0sq~~d 0x11 Eyxb1ac~vcp|1rp~e 0x12 Fz{a2b`}u`s2qs||}f 0x13 G{z`3ca|tar~3pr}}|g 0x14 @|}g4df{sfuy4wuzz{` 0x15 A}|f5egzrgtx5vt{{za 0x16 B~e6fdyqdw{6uwxxyb 0x17 C~d7gexpevz7tvyyxc 0x18 Lpqk8hjwjyu8{yvvwl 0x19 Mqpj9ikv~kxt9zxwwvm 0x1a Nrsi:jhu}h{w:y{ttun 0x1b Osrh;kit|izv;xzuuto 0x1c Htuo<lns{n}q<}rrsh 0x1d Iutn=morzo|p=~|ssri 0x1e Jvwm>nlqyls>}ppqj 0x1f Kwvl?ompxm~r?|~qqpk 0x20 t H I S P R O G R A M C A N N O T 0x21 u!I!H!R!!Q!S!N!F!S!@!L!!B!@!O!O!N!U! 0x22 v"J"K"Q""R"P"M"E"P"C"O""A"C"L"L"M"V" 0x23 w#K#J#P##S#Q#L#D#Q#B#N##@#B#M#M#L#W# 0x24 p$L$M$W$$T$V$K$C$V$E$I$$G$E$J$J$K$P$ 0x25 q%M%L%V%%U%W%J%B%W%D%H%%F%D%K%K%J%Q% 0x26 r&N&O&U&&V&T&I&A&T&G&K&&E&G&H&H&I&R& 0x27 s'O'N'T''W'U'H'@'U'F'J''D'F'I'I'H'S' 0x28 |(@(A([((X(Z(G(O(Z(I(E((K(I(F(F(G(\( 0x29 })A)@)Z) )Y)[)F)N)[)H)D) )J)H)G)G)F)]) 0x2a ~*B*C*Y* *Z*X*E*M*X*K*G* *I*K*D*D*E*^* 0x2b +C+B+X+ +[+Y+D+L+Y+J+F+ +H+J+E+E+D+_+ 0x2c x,D,E,_, ,\,^,C,K,^,M,A, ,O,M,B,B,C,X, 0x2d y-E-D-^- -]-_-B-J-_-L-@- -N-L-C-C-B-Y- 0x2e z.F.G.]..^.\.A.I.\.O.C..M.O.@.@.A.Z. 0x2f {/G/F/\//_/]/@/H/]/N/B//L/N/A/A/@/[/ 0x30 d0X0Y0C00@0B0_0W0B0Q0]00S0Q0^0^0_0D0 0x31 e1Y1X1B11A1C1^1V1C1P1\11R1P1_1_1^1E1 0x32 f2Z2[2A22B2@2]2U2@2S2_22Q2S2\2\2]2F2 0x33 g3[3Z3@33C3A3\3T3A3R3^33P3R3]3]3\3G3 0x34 `4\4]4G44D4F4[4S4F4U4Y44W4U4Z4Z4[4@4 0x35 a5]5\5F55E5G5Z5R5G5T5X55V5T5[5[5Z5A5 0x36 b6^6_6E66F6D6Y6Q6D6W6[66U6W6X6X6Y6B6 0x37 c7_7^7D77G7E7X7P7E7V7Z77T7V7Y7Y7X7C7 0x38 l8P8Q8K88H8J8W8_8J8Y8U88[8Y8V8V8W8L8 0x39 m9Q9P9J99I9K9V9^9K9X9T99Z9X9W9W9V9M9 0x3a n:R:S:I::J:H:U:]:H:[:W::Y:[:T:T:U:N: 0x3b o;S;R;H;;K;I;T;\;I;Z;V;;X;Z;U;U;T;O; 0x3c hV>W>M>>N>L>Q>Y>L>_>S>>]>_>P>P>Q>J> 0x3f k?W?V?L??O?M?P?X?M?^?R??\?^?Q?Q?P?K? 0x40 @(@)@3@`@0@2@/@'@2@!@-@`@#@!@.@.@/@4@ 0x41 A)A(A2AaA1A3A.A&A3A A,AaA"A A/A/A.A5A 0x42 B*B+B1BbB2B0B-B%B0B#B/BbB!B#B,B,B-B6B 0x43 C+C*C0CcC3C1C,C$C1C"C.CcC C"C-C-C,C7C 0x44 D,D-D7DdD4D6D+D#D6D%D)DdD'D%D*D*D+D0D 0x45 E-E,E6EeE5E7E*E"E7E$E(EeE&E$E+E+E*E1E 0x46 F.F/F5FfF6F4F)F!F4F'F+FfF%F'F(F(F)F2F 0x47 G/G.G4GgG7G5G(G G5G&G*GgG$G&G)G)G(G3G 0x48 H H!H;HhH8H:H'H/H:H)H%HhH+H)H&H&H'HJ 0x4b K#K"K8KkK;K9K$K,K9K*K&KkK(K*K%K%K$K?K 0x4c L$L%L?LlLL#L+L>L-L!LlL/L-L"L"L#L8L 0x4d M%M$M>MmM=M?M"M*M?M,M MmM.M,M#M#M"M9M 0x4e N&N'N=NnN>NP>P?P$P 0x51 Q9Q8Q"QqQ!Q#Q>Q6Q#Q0QQ%Q 0x52 R:R;R!RrR"R R=R5R R3R?RrR1R3RSsS0S2S=S=SV?V%VvV&V$V9V1V$V7V;VvV5V7V8V8V9V"V 0x57 W?W>W$WwW'W%W8W0W%W6W:WwW4W6W9W9W8W#W 0x58 X0X1X+XxX(X*X7X?X*X9X5XxX;X9X6X6X7X,X 0x59 Y1Y0Y*YyY)Y+Y6Y>Y+Y8Y4YyY:Y8Y7Y7Y6Y-Y 0x5a Z2Z3Z)ZzZ*Z(Z5Z=Z(Z;Z7ZzZ9Z;Z4Z4Z5Z.Z 0x5b [3[2[([{[+[)[4[<[)[:[6[{[8[:[5[5[4[/[ 0x5c \4\5\/\|\,\.\3\;\.\=\1\|\?\=\2\2\3\(\ 0x5d ]5]4].]}]-]/]2]:]/]<]0]}]>]<]3]3]2])] 0x5e ^6^7^-^~^.^,^1^9^,^?^3^~^=^?^0^0^1^*^ 0x5f _7_6_,__/_-_0_8_-_>_2__<_>_1_1_0_+_ 0x60 4`` ``@``````` `@``````` 0x61 5a aaaAaaaaaaa aAaaaaaaa 0x62 6b b bbBbbb bbbbbBbbb b b bb 0x63 7c c ccCccc cccccCccc c c cc 0x64 0d d ddDddd dddd dDddd d d dd 0x65 1e e eeEeee eeeeeEeee e e ee 0x66 2ffffFfff ffff fFfffff ff 0x67 3ggggGggggggg gGggg g ggg 0x68 jjjjJjjjj jj jjJj j jjjjj 0x6b ?kkkkKkkkk kk kkKkk kkkkk 0x6c 8llllLllll ll llLll lllll 0x6d 9mmmmMmmmm mm mmMmm mmmmm 0x6e :nnnnNnnnn nnnnNn nnnnnn 0x6f ;ooooOooooooooOo oooooo 0x70 $ppppPppppppppPppppppp 0x71 %qqqqQqqqqqqqqQqqqqqqq 0x72 &rrrrRrrrrrrrrRrrrrrrr 0x73 'ssssSssssssssSsssssss 0x74 ttttTttttttttTttttttt 0x75 !uuuuUuuuuuuuuUuuuuuuu 0x76 "vvvvVvvvvvvvvVvvvvvvv 0x77 #wwwwWwwwwwwwwWwwwwwww 0x78 ,xxx xXxx xxx xxxXxxxxxx x 0x79 -yyy yYy y yyy yyyYyyyyyy y 0x7a .zzz zZz zzzzzzzZzzzzzzz 0x7b /{{{{[{ { {{{ {{{[{{{{{{{ 0x7c (||||\| |||||||\||||||| 0x7d )}}}}]} }}}}}}}]}}}}}} } 0x7e *~~~ ~^~~ ~~~ ~~~^~~~~~~ ~ 0x7f + _  _  0x80 Ԁ󀠀퀠 0x81 Ձ򁡁쁡 0x82 ւ񂢂 0x83 ׃ 0x84 Є鄤 0x85 х腥 0x86 ҆놦 0x87 Ӈ􇧇ꇧ 0x88 ܈刨 0x89 ݉䉩 0x8a ފ犪 0x8b ߋ拫 0x8c ،ጬ 0x8d ٍ 0x8e ڎ㎮ 0x8f ۏ⏯ 0x90 Đ㐰 0x91 ő⑱ 0x92 ƒᒲ 0x93 Ǔ 0x94 甴 0x95 敵 0x96 –営 0x97 ×䗷 0x98 ̘똸 0x99 ͙ꙹ 0x9a Κ隺 0x9b ϛ蛻 0x9c Ȝ񜼜 0x9d ɝ𝽝 0x9e ʞힾ󞾞 0x9f ˟쟿򟿟 0xa0 ȠɠӠРҠϠǠҠ͠àΠΠϠԠ 0xa1 ɡȡҡѡӡΡơӡ̡¡ϡϡΡա 0xa2 ʢˢѢҢТ͢ŢТâϢâ̢̢֢͢ 0xa3 ˣʣУӣѣ̣ģѣ£Σ£̣ͣͣף 0xa4 ̤ͤפԤ֤ˤä֤ŤɤǤŤʤʤˤФ 0xa5 ̥֥ͥեץʥ¥ץĥȥƥĥ˥˥ʥѥ 0xa6 ΦϦզ֦ԦɦԦǦ˦ŦǦȦȦɦҦ 0xa7 ϧΧԧקէȧէƧʧħƧɧɧȧӧ 0xa8 ۨبڨǨϨڨɨŨ˨ɨƨƨǨܨ 0xa9 ک٩۩ƩΩ۩ȩĩʩȩǩǩƩݩ 0xaa ªê٪ڪتŪͪت˪Ǫɪ˪ĪĪŪު 0xab ë«ث۫٫ī̫٫ʫƫȫʫūūī߫ 0xac ĬŬ߬ܬެìˬެͬϬͬ¬¬ìج 0xad ŭĭޭݭ߭­ʭ̭߭έ̭íí­٭ 0xae ƮǮݮޮܮɮܮϮîͮϮڮ 0xaf ǯƯܯ߯ݯȯݯί¯̯ίۯ 0xb0 ذٰð°߰װ°ѰݰӰѰްް߰İ 0xb1 ٱر±ñޱֱñбܱұб߱߱ޱű 0xb2 ڲ۲²ݲղӲ߲ѲӲܲܲݲƲ 0xb3 ۳ڳóܳԳҳ޳гҳݳݳܳdz 0xb4 ܴݴǴĴƴ۴Ӵƴմٴ״մڴڴ۴ 0xb5 ݵܵƵŵǵڵҵǵԵصֵԵ۵۵ڵ 0xb6 ޶߶ŶƶĶٶѶĶ׶۶ն׶ضضٶ¶ 0xb7 ߷޷ķǷŷطзŷַڷԷַٷٷط÷ 0xb8 иѸ˸ȸʸ׸߸ʸٸո۸ٸָָ׸̸ 0xb9 ѹйʹɹ˹ֹ޹˹عԹڹع׹׹ֹ͹ 0xba ҺӺɺʺȺպݺȺۺ׺ٺۺԺԺպκ 0xbb ӻһȻ˻ɻԻܻɻڻֻػڻջջԻϻ 0xbc Լռϼ̼μӼۼμݼѼ߼ݼҼҼӼȼ 0xbd սԽνͽϽҽڽϽܽн޽ܽӽӽҽɽ 0xbe ־׾;ξ̾Ѿپ̾߾Ӿݾ߾ооѾʾ 0xbf ׿ֿ̿ϿͿпؿͿ޿ҿܿ޿ѿѿп˿ 0xc0 0xc1 0xc2 ª«±²°­¥°£¯¡£¬¬­¶ 0xc3 ëêðóñìäñâîàâííì÷ 0xc4 ĬĭķĴĶīģĶĥĩħĥĪĪīİ 0xc5 ŭŬŶŵŷŪŢŷŤŨŦŤūūŪű 0xc6 ƮƯƵƶƴƩơƴƧƫƥƧƨƨƩƲ 0xc7 ǯǮǴǷǵǨǠǵǦǪǤǦǩǩǨdz 0xc8 ȠȡȻȸȺȧȯȺȩȥȫȩȦȦȧȼ 0xc9 ɡɠɺɹɻɦɮɻɨɤɪɨɧɧɦɽ 0xca ʢʣʹʺʸʥʭʸʫʧʩʫʤʤʥʾ 0xcb ˣˢ˸˻˹ˤˬ˹˪˦˨˪˥˥ˤ˿ 0xcc ̸̡̢̢̤̥̼̣̫̭̯̭̣̿̾̾ 0xcd ͥͤ;ͽͿͪ͢Ϳͬͮͬͣͣ͢͠͹ 0xce ΦΧνξμΡΩμίΣέίΠΠΡκ 0xcf ϧϦϼϿϽϠϨϽϮϢϬϮϡϡϠϻ 0xd0 ийУРТпзТбнгбоопФ 0xd1 ѹѸѢѡѣѾѶѣѰѼѲѰѿѿѾѥ 0xd2 ҺһҡҢҠҽҵҠҳҿұҳҼҼҽҦ 0xd3 ӻӺӠӣӡӼӴӡӲӾӰӲӽӽӼӧ 0xd4 ԼԽԧԤԦԻԳԦԵԹԷԵԺԺԻԠ 0xd5 սռզեէպղէմոնմջջպա 0xd6 ־ֱֵַַָָֹֹֻֿ֥֦֤֤֢ 0xd7 ׿׾פקץ׸װץ׶׺״׶׹׹׸ף 0xd8 ذرثبتطؿتعصػعضضطج 0xd9 ٱٰ٪٩٫ٶپ٫ٸٴٺٸٷٷٶ٭ 0xda ڲڳکڪڨڵڽڨڻڷڹڻڴڴڵڮ 0xdb ۳۲ۨ۫۩۴ۼ۩ۺ۶۸ۺ۵۵۴ۯ 0xdc ܴܵܯܬܮܻܳܮܱܽܿܽܲܲܳܨ 0xdd ݵݴݮݭݯݲݺݯݼݰݾݼݳݳݲݩ 0xde ޶޷ޭޮެޱ޹ެ޿޳޽޿ްްޱު 0xdf ߷߶߬߯߭߰߸߭߾߲߼߾߱߱߰߫ 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef 0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff yara-4.1.3/tests/gcov-summary000077500000000000000000000020271413423160300161720ustar00rootroot00000000000000#!/bin/sh # pseudo code: # - find all .c files in libyara/ # - run gcov --preserve-paths # - fold gcov output into single summary line output # - remember files not executed, and percentages # - at end: output grand total percentage # - at end: output files not executed cd libyara/ find . -type f | egrep "\.c$" | perl -lane ' if (-e $_) { $cmd = qq[gcov --preserve-paths $_]; $out = `$cmd 2>&1`; $out =~ s~(Creating|[^\n]+cannot open notes) [^\n]+\s+~~gs; @out = split(m~\n~, $out); foreach(reverse @out) { if (m~assuming not executed~) { push @assume, $_; next; } if (m~Lines executed:([\d\.]+). of (\d+)~) { $loc_tot += $2; $loc_exe += ($2 * $1 / 100); } printf qq[%-32s%s], $_, m~^Lines~ ? q[] : qq[\n]; } } sub END { if ($loc_tot) { printf qq[Lines executed:%.2f%% of %u total lines in all executed code\n\n], $loc_exe / $loc_tot * 100, $loc_tot; } foreach(@assume) { printf qq[%s\n], $_; } }' exit 0 yara-4.1.3/tests/oss-fuzz/000077500000000000000000000000001413423160300154125ustar00rootroot00000000000000yara-4.1.3/tests/oss-fuzz/dex_fuzzer.cc000066400000000000000000000014631413423160300201120ustar00rootroot00000000000000#include #include #include YR_RULES* rules = NULL; extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { YR_COMPILER* compiler; if (yr_initialize() != ERROR_SUCCESS) return 0; if (yr_compiler_create(&compiler) != ERROR_SUCCESS) return 0; if (yr_compiler_add_string(compiler, "import \"dex\"", NULL) == 0) yr_compiler_get_rules(compiler, &rules); yr_compiler_destroy(compiler); return 0; } int callback( YR_SCAN_CONTEXT* context, int message, void* message_data, void* user_data) { return CALLBACK_CONTINUE; } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (rules == NULL) return 0; yr_rules_scan_mem( rules, data, size, SCAN_FLAGS_NO_TRYCATCH, callback, NULL, 0); return 0; } yara-4.1.3/tests/oss-fuzz/dex_fuzzer_corpus/000077500000000000000000000000001413423160300211725ustar00rootroot000000000000001cf540db2f048bb21bd89379a57279b9ff4c308558715a3baee666a47393d86e000066400000000000000000001025741413423160300316200ustar00rootroot00000000000000yara-4.1.3/tests/oss-fuzz/dex_fuzzer_corpusdex 037Q' Oh bҝLO|pxV4Qpi uX &|k| LLLL#L(L2LVLLLLLLLLLLLLLLM MMMM)MBMbMMMMMM NNPNyNNNNNOO5O8O;O>OAOKOSOWO]OOOOOOOOOOP1PCPpPPP Q8QVQQQQQQQQQQQRRR RRRRR#R(R,RDRcRRRRR S.SIS|SSSS T0T]TTTTU1UlUUUUUVV4VQVkVVVVVW&W?WTWkWWWWWXJXuXX YYYYZZZZ[3[V[u[[[[[[\4\N\e\|\\\\\\]%]:]P]r]]]]]]]^#^:^[^y^^^^^^_#_9_P_h_z________`)`I`h````````````````aa aa+a.a4a8a=aCaHaMaQabawaaaab bb!b&bYbfbpbbbbbbbb,cJcOcucccccccccccddd!d/d8dZdtd|dddddde8eaeeeeeeeeeeeeef ff$f3fDfPf_fgfwf|ffffffffffffgg!g3g:g>gFgQgYgpg}ggggggghhh'h-h4hlHl]ldlnlwllllllllll0m:mNmamlmymmmmmmn8nUnunnnnn3o9oCoToio{ooooooooop ppp"p*p0p5p?pHpQp[pqpvppppppppppqqq$q:qWqwqqqqqqqq rr(r0r;rTrZrjrrrrrrrrrrsss9sEs_srszsssssssssstttt"t1t@t\tltytttttttttu*u7uPucuvuuuuuuuuuuu v0vC/s V?!q A(!1W!2W!]"#h#g$ $ %" &f'Y'i(u(@#)@)&])#d)#)N)@)@!,'-? -m-H-O,.@.m.@/n^/(g/1s0?00?10?R0Y0]0YO0]O1R11j2S12F3R13F4Q14?5P15?6R16Z7<77<77R7R7?17M7M7Y7K7?27h37?;7$M7?N7Z7`7a7.b7m7eo7p7r7-t7%v7L|7_7e7n7e7e7i7i7e7e7e7e7?7?7J7a7Y7?707e7G7@7?7?7a7@ 7B"7G+7?-7?.7??>X1>?>.??1?.=?cO@Y1@e)@eB@.X@.j@.n@e@ @r@s@pAT1A?A;_CU1F?F GV1IW1I?IYOM?M.=N*HO=Q?1Sb1Sc1SSkSn<Sl>Sn?S3SSS2St*Sn/S.>S/HT?1T5T@T.=U?1U6U7U8U.=Vd V %Y:YqY9QZ?ZZ '[Y1[4S\?1\`1\l\l\l\']l]?]l]'^A1_?1`e{`+a?1ala,Wb'0Q71QJxIc2QJIt3QJI4QJI5QJI6I7I҃Ă˂ւ.w i"0pQiwp"Tp[ "[p [ bp0T  wq"@p c"I"Cp06p0eTTn n eTn n0c8nT2 ' T2bq 78n( bq 7( bq 7( 8n' bq 7( 2( T2( ( 2( *2 9> EZ8M;hEIEPWE^MlWMnexbbp0T x>q Ts "^p0n C q 6TtTs"Upn % n e n n e n n S '$;xbbp0Vx"UpPn  n @ n p0T2pRx[ pxn  x[ [ p xNTb !#51DFn0@ n =Td qe n  8("Up9n T n  n qSTd qe n (Ta qhy[ p!y rT qh-y [[Yp9yhTcT3nw Tdn0&C T3TdTDnw n C TcT3ns q EC TcT3T3Rdn C -n HTcT3T3Rdn C -nFTcT3T3Rdn C -n I# =qS(`jy[pry T TT$n Jyy[pyw"Upn Q  n ! n a n qS3TAn QTAqd q f!TAqd TATn  -"4p@]Bn G!TAT$"5p _Bn K!Xq /2TAqd TBqc n 3! qSTAqgTRT%YppyFp$[ Y!\!!\![ [ [ [ "\p[ %"\p[ "1p W [ \! Y!#\!"_p[ y nA 8q m yn? RAYn Bn C#n D# y2"@p n 8'"Upn n e n e n qSn 9!6E5eFn 8 n p lx(T8Tn P 9!%5TFU8T8Tn =Tn n v 8p  8hT8!Tn =Tn n v 8 p  8Tn T8=Tn =5Tn n v 8)p  9p  8'"Upn n v n v n qS)vTn ("Upn n v n v n qSTn (n p h 8"Upn n v  n v n qS(Tn (Cz n< n =Nz1n {# 8 2qSn gz1n { 8 ;qSn vz q kq jz  nw "6p ar r 8r @n n0% 8Tx n 8@ n0# 86RY"UpTyn  n n qSRn 3qSp ( (qN  @n O ?n 8 8'q,  nY("UpTyn  n n qS)jq,  nY((RY"Up n n n n qSRn 36qSp )+2lzBq0 89;n  9.7n  9%6n  9:n  9<n  9 5n  (( { Tn 8{6"@p 2 n@%C 8n ! 8qS qS(GqSM#C{ "@p n I{9nw n n0%2 9 qSTn0#2 "Upn C Tn C n qSk{#nw n n0%! 9 qSTRTr 2 8{Z"@p 2n 8; n@%C "UpGn C n  n qS8n 1 8\S!n 9EqSUS! qS( qS( ); KMJMR{$q "Up4n T n  n qS92( M"{$nw " p Rn Rn 2n0)  r =C({" \p [ tv " Up $n n - n qS  "p l" Up !n n -  n n p l *p l )p l 'p l" Up (n n -  n n p lv 9 U 9 v 9  +p l" \T p r r 8 Sr @n 1 9 r r 8 r @n n n 8 " Up n n n n qS T n =(U 8 tw " \T p r r 8 r @n n0%  8 T8 Tn0& 8 FT8 Bq 8 tRR56TTn 3+" Up >n n n n qS T n }Tn q 9 9 T Tr 8 " Up n n n n qS T n })aRR6TTn 2 TTn 9 " Up Cn n n n qS T n }Tn ( " Up n n n n qS q 9 9 T Tr 8 " Up n n n n qS T n }) T n }" Up )n n n n qS q 9 9 T Tr 8 " Up n n n n qS T n })m q 929 0TTr 8$"Upn n n n qSTn ~' T r r 8 $r @" Up n n n n qS (5MD T" ~P |q 1 "@p n 8X["@Tp qn 8E">"G"Ap  p0 p vn 8lTn n ( e"UpIn Tn n qSnn8 n t 8"@#p n 8 #[("Upn G n n [)z$[)sne( n ();iMKMMA}"@p n 8n ( M} (,q 2CU 8+"Upn C R#n C n qSR#8nu R#q05CqUT8Tn 8 qUq /T.q 2Cnx 9#q. q  q3 r !2qSp qSU!9 " p 1n p 8 p 9p U8nw "p0T%n@*Cenw "7p0 n@*Cx" p0C n C  n C n :OqUpp nr OqU n( n(_'MM }"@%p n 8nw ">"G" Ap ) p0 p n 8dn = n 9n !8)3FF  b n@'( n"Up wn  F  n  n F  n n qS( vJqSnnnv( n ( %F R+MQM~MM$~Mq. q8 r@9A# n-( =~"Upn  U!n  n qSn q .[ $"ap[ T!n q -n T!n q -n pqN Cn M 8p 9q 8pT n 9qSppT $T!n n LT p ~ `~  "@&p rn 8\">"G"Ap ( p0 p tn 8un = n u 9T%n W 9"Upn n W n qST%n W( Cn qS8nT%r r 8;r S"Up+n n W n qSnw n@"W(8nC( ( ( 8n' ( C( (#9_h M]MMMMn: 9n; A3 NqS   "@ 5p "Ap gn 8@n q n  9  qS " Sn  Yn p " Up  n n n qS 8n8n  qS  n(  qS 8n8n( n( 8n8n' n( ( ( e(&?Ypx BbMjEoMMEBn0z Pr0  UqN An MT \dUd8>qN Bn OT n @ !5CFn T 8"UpTen T Fn T n [d(Fn T 8"UpTen T Fn T n [d(F n T 8"UpTen T Fn T n [d("UpTen T Fn T n [d("Upn T Ten T n qS"Upn T Ten T n qS"Upn T Ten T n qS"Upn T Ten T n qSTd8Tdn = qSTd8 Tdn <Td8 Tdn <Td8 Tdn <qS(qS(z3n0z& [b"TbrTb"!r02 =5TbTc"q r0C r 2(  o n -n { n  8DqSn pn|  Hn >pnnt  q n| n? n| n BXn| n@q}q0 8 7n 9n q0 "Up n n x n qS"Up fn nx n n qS8 .n 9nx 8)\Un pnU8Qq 89n 93qSp\8-n 8 HqS(qS -q 2q /(UqS -q 2q /p \ "Up n U n n qSU 8?nu  q04 Y#"Up n R#n  n n qSR#8 nu   q05 q 8n FqSpp(5AM( 8 n| Hn >4JTB"9nTBr R 9<TBr TBr RTB"r q r0 !r0 r"Up n 2 n R n qU X"Ap t <  1 = b9` 1 = 8  1 ;  #en 4 <  n 6 " S p@<}n 9" S p@< n " Up " Sp@=n n n n ;h8Y59 { #en 4 !<29 <  n 9 " S p@