[
  {
    "path": ".gitattributes",
    "content": "notebooks/* linguist-vendored\ninclude/* linguist-vendored\nbindings/* linguist-vendored\n*.h linguist-detectable=false\n*.cpp linguist-detectable=false\n"
  },
  {
    "path": ".github/release-template.ejs",
    "content": "<% var groupCommits = [\n{\n    name: 'breaking',\n    show: true,\n    list: []\n}, {\n    name: 'feat',\n    show: true,\n    list: []\n}, {\n    name: 'perf',\n    show: true,\n    list: []\n}, {\n    name: 'fix',\n    show: true,\n    list: []\n}, {\n    name: 'refactor',\n    show: true,\n    list: []\n}, {\n    name: 'docs',\n    show: true,\n    list: []\n},  {\n    name: 'test',\n    show: true,\n    list: []\n}, {\n    name: 'other',\n    show: true,\n    list: []\n}\n]\n\nvar all_titles = {};\nvar all_commiters = {};\nvar commitHref = \"https://github.com/jina-ai/docarray/commit/\"\n\ncommits.forEach(function (commit) {\n\n    var result = (commit.title).match(/^(\\w*)(\\((.*)\\))?\\: (.*)$/);\n\n    var type = result && result[1];\n    var scope = result && result[3];\n    var title = result && result[4];\n    var committer = commit.authorName\n\n    if (!(committer in all_commiters)) {\n        all_commiters[committer] = 1\n    }\n\n    if (!(title in all_titles)) {\n        all_titles[title] = 1\n        if( title != null && (title.indexOf('💥')>-1 || title.indexOf(':boom:')>-1) ){\n            groupCommits.find(item => item.name === 'breaking').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else if(type == 'fix' || type == 'fixed'){\n            groupCommits.find(item => item.name === 'fix').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else if(type == 'perf' || type == 'performance'){\n            groupCommits.find(item => item.name === 'perf').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else if(type == 'feat' || type == 'feature'){\n            groupCommits.find(item => item.name === 'feat').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else if(type == 'refactor'){\n            groupCommits.find(item => item.name === 'refactor').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else if(type == 'docs' || type == 'doc'){\n            groupCommits.find(item => item.name === 'docs').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else if(type == 'test' || type == 'tests' || type == 'ci'){\n            groupCommits.find(item => item.name === 'test').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        } else {\n            groupCommits.find(item => item.name === 'other').list.push({\n                type: type,\n                scope: scope,\n                title: title,\n                commit: commit\n            })\n        }\n    }\n\n\n});\n\n\nvar listCommits = function(list, key){\n\nlist.forEach(function (ct) {\n\n    var type = ct.type;\n    var scope = ct.scope;\n    var title = '';\n    var commit = ct.commit;\n\n    if(type){\n        if(key != 'other'){\n            title = (scope? '__'+scope+'__: ':'') + ct.title;\n        }else{\n            title = '__' + type + (scope? '('+scope+')':'') + '__ : ' + ct.title;\n        }\n    }else{\n        title = commit.title;\n    }\n%> - <% if(typeof commitHref === 'undefined' || commitHref === '') { %>[```<%=commit.sha1.slice(0,8)%>```]<% } else { %>[[```<%=commit.sha1.slice(0,8)%>```](<%=commitHref%><%=commit.sha1%>)]<%}%> __-__ <%=title%> (*<%= commit.authorName %>*)\n<% })} %>\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n<%  Object.keys(all_commiters).forEach(function (key) { %> <%= key %>, <% }) %> 🙇\n\n<%\n        for(var i of groupCommits){\n    if(i.list.length == 0) continue;\n\nif (i.name === 'breaking' && i.show) { %>\n### 💥 Breaking changes\n\n<%\t} else if (i.name === 'fix' && i.show) { %>\n### 🐞 Bug fixes\n\n<%\t} else if( i.name === 'feat' && i.show) { %>\n### 🆕 New Features\n\n<%\t} else if(i.name === 'perf' && i.show) { %>\n### ⚡ Performance Improvements\n\n<%\t} else if(i.name === 'refactor' && i.show) { %>\n### 🧼 Code Refactoring\n\n<%\t} else if(i.name === 'docs' && i.show) { %>\n### 📗 Documentation\n\n<%\t} else if(i.name === 'test' && i.show) { %>\n### 🏁 Unit Test and CICD\n\n<%\t} else if (i.name === 'other' && i.show) { %>\n### 🍹 Other Improvements\n\n<%\t}\n    i.show && listCommits(i.list, i);\n} %>\n"
  },
  {
    "path": ".github/requirements-test.txt",
    "content": "pytest\npytest-custom_exit_code\n"
  },
  {
    "path": ".github/workflows/cd.yml",
    "content": "name: CD\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  prep-testbed:\n    if: |\n      !startsWith(github.event.head_commit.message, 'chore') &&\n      !startsWith(github.event.head_commit.message, 'build: hotfix') &&\n      !endsWith(github.event.head_commit.message, 'reformatted by jina-dev-bot')\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - id: set-matrix\n        run: |\n          sudo apt-get install jq\n          echo \"::set-output name=matrix::$(bash scripts/get-all-test-paths.sh)\"\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n\n  build-wheels:\n    needs: [prep-testbed]\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, windows-latest, macos-latest]\n        cibw_arch: [\"auto64\"]\n        python-version: [['3.7', \"cp37-*\"], ['3.8', \"cp38-*\"], ['3.9', \"cp39-*\"], ['3.10', \"cp310-*\"]]\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 100\n      - name: Set up Python ${{ matrix.python-version[0] }}\n        uses: actions/setup-python@v4\n        with:\n          python-version: ${{ matrix.python-version[0] }}\n      - name: Update version\n        shell: bash\n        run: |\n          git fetch --depth=1 origin +refs/tags/*:refs/tags/*\n          ./scripts/update-version.sh\n      - name: Build sdist\n        if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version[0] == '3.7' }}\n        run: |\n          pip install build\n          python -m build --sdist\n      - name: Build wheels\n        uses: pypa/cibuildwheel@v2.10.2\n        with:\n          package-dir: ./\n        env:\n          CIBW_ENVIRONMENT: >\n            STAN_BACKEND=\"${{ env.STAN_BACKEND }}\"\n          CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014\n          CIBW_BUILD: ${{ matrix.python-version[1] }}\n          CIBW_SKIP: \"*musllinux*\"\n          CIBW_ARCHS: ${{ matrix.cibw_arch }}\n          # CIBW_ARCH_MACOS: x86_64 arm64\n          CIBW_BUILD_FRONTEND: build\n      - uses: actions/upload-artifact@v3\n        with:\n          path: |\n            ./wheelhouse/*.whl\n            ./dist/*.tar.gz\n\n  core-test:\n    needs: [ prep-testbed, build-wheels ]\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ ubuntu-latest, windows-latest, macos-latest ]\n        cibw_arch: [ \"auto64\" ]\n        python-version: [ [ '3.7', \"cp37-*\" ] ]\n        test-path: ${{fromJson(needs.prep-testbed.outputs.matrix)}}\n    env:\n      JINA_HIDE_SURVEY: \"1\"\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 100\n      - name: Set up Python ${{ matrix.python-version[0] }}\n        uses: actions/setup-python@v4\n        with:\n          python-version: ${{ matrix.python-version[0] }}\n      - name: Prepare enviroment\n        run: |\n          python -m pip install --upgrade pip\n          pip install jina\n          pip install --pre docarray\n          pip install pytest pytest-html pytest-cov pytest-mock pytest-repeat pytest-custom-exit-code pytest-timeout pytest-reraise\n      - uses: actions/download-artifact@v3\n        with:\n          name: artifact\n      - name: Install annlite linux\n        if: ${{ matrix.os == 'ubuntu-latest' }}\n        run: |\n          pip install wheelhouse/*${{ matrix.python-version[1] }}**linux*.whl\n      - name: Install annlite macos\n        if: ${{ matrix.os == 'macos-latest' }}\n        run: |\n          pip install wheelhouse/*${{ matrix.python-version[1] }}**macos**x86_64*.whl\n      - name: Install annlite win\n        if: ${{ matrix.os == 'windows-latest'}}\n        run: |\n          pip install --pre --find-links=wheelhouse/ annlite\n\n      - name: Test unix\n        id: test_unix\n        if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' }}\n        run: |\n          cd ..\n          mv annlite/tests/ ./\n          pytest --suppress-no-test-exit-code --cov=annlite --cov-report=xml \\\n          -v -s -m \"not gpu\" ${{ matrix.test-path }}\n          echo \"::set-output name=codecov_flag::annlite\"\n        timeout-minutes: 30\n      - name: Test win\n        id: test_win\n        if: ${{ matrix.os == 'windows-latest'}}\n        env:\n          PYTHONIOENCODING: 'utf-8'\n        run: |\n          cd ..\n          move annlite/tests/ ./\n          cd tests/\n          pytest -v -s -m \"not gpu\" -k \"test\"\n        timeout-minutes: 30\n      - name: Check codecov file\n        id: check_files\n        uses: andstor/file-existence-action@v1\n        with:\n          files: \"coverage.xml\"\n      - name: Upload coverage from test to Codecov\n        uses: codecov/codecov-action@v2\n        if: steps.check_files.outputs.files_exists == 'true' && ${{ matrix.python-version[0] }} == '3.7' && ${{ matrix.matrix.os }} == 'ubuntu-latest'\n        with:\n          file: coverage.xml\n          flags: ${{ steps.test.outputs.codecov_flag }}\n          fail_ci_if_error: false\n          token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos\n\n  prerelease:\n    needs: [core-test]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 100\n      - uses: actions/download-artifact@v3\n        with:\n          name: artifact\n      - name: Pre-release (.devN)\n        run: |\n          git fetch --depth=1 origin +refs/tags/*:refs/tags/*\n          pip install twine\n          ./scripts/release.sh\n        env:\n          TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}\n          TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}\n          JINA_SLACK_WEBHOOK: ${{ secrets.JINA_SLACK_WEBHOOK }}\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  pull_request:\n\njobs:\n  commit-lint:\n    runs-on: ubuntu-latest\n    steps:\n      - name: find the prev warning if exist\n        uses: peter-evans/find-comment@v1\n        id: fc\n        with:\n          issue-number: ${{ github.event.pull_request.number }}\n          comment-author: \"github-actions[bot]\"\n          body-includes: \"bad commit message\"\n      - name: Delete comment if exist\n        if: ${{ steps.fc.outputs.comment-id != 0 }}\n        uses: actions/github-script@v3\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          script: |\n            github.issues.deleteComment({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              comment_id: ${{ steps.fc.outputs.comment-id }},\n            })\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0\n      - run: 'echo \"module.exports = {extends: [''@commitlint/config-conventional'']}\" > commitlint.config.js'\n      - uses: wagoid/commitlint-github-action@v1\n        env:\n          GITHUB_TOKEN: \"${{ secrets.GITHUB_TOKEN }}\"\n      - name: if lint failed\n        if: ${{ failure() }}\n        uses: peter-evans/create-or-update-comment@v1\n        with:\n          issue-number: ${{ github.event.pull_request.number }}\n          body: |\n            Thanks for your contribution :heart:\n            :broken_heart: Unfortunately, this PR has one ore more **bad commit messages**, it can not be merged. To fix this problem, please refer to:\n            - [Commit Message Guideline for the First Time Contributor](https://github.com/jina-ai/jina/issues/553)\n            - [Contributing Guideline](https://github.com/jina-ai/jina/blob/master/CONTRIBUTING.md)\n\n            Note, other CI tests will *not* *start* until the commit messages get fixed.\n\n            This message will be deleted automatically when the commit messages get fixed.\n          reaction-type: \"eyes\"\n\n#  lint-flake-8:\n#    runs-on: ubuntu-latest\n#    steps:\n#      - uses: actions/checkout@v2\n#      - name: Set up Python 3.7\n#        uses: actions/setup-python@v2\n#        with:\n#          python-version: 3.7\n#      - name: Lint with flake8\n#        run: |\n#          pip install flake8\n#          # stop the build if there are Python syntax errors or undefined names\n#          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude .git,__pycache__,docs/source/conf.py,old,build,dist,tests/,jina/resources/,bindings\n#          # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide\n#          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude .git,__pycache__,docs/source/conf.py,old,build,dist,tests/,jina/resources/\n\n  check-black:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 0\n      - name: Set up Python 3.7\n        uses: actions/setup-python@v2\n        with:\n          python-version: 3.7\n      - id: file_changes\n        uses: Ana06/get-changed-files@v1.2\n      - name: check black\n        run: ./scripts/black.sh\n        env:\n          CHANGED_FILES: ${{ steps.file_changes.outputs.added_modified }}\n\n  prep-testbed:\n    runs-on: ubuntu-latest\n    needs: [commit-lint, check-black]\n    steps:\n      - uses: actions/checkout@v2\n      - id: set-matrix\n        run: |\n          sudo apt-get install jq\n          echo \"::set-output name=matrix::$(bash scripts/get-all-test-paths.sh)\"\n    outputs:\n      matrix: ${{ steps.set-matrix.outputs.matrix }}\n\n  core-test:\n    needs: [prep-testbed]\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, windows-latest]\n        cpython-version: [\"cp37-*\"]\n        python-version: [3.7]\n        cibw_arch: [\"auto64\"]\n        test-path: ${{fromJson(needs.prep-testbed.outputs.matrix)}}\n    env:\n      JINA_HIDE_SURVEY: \"1\"\n    steps:\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v3\n        with:\n          python-version: ${{ matrix.python-version }}\n      - uses: actions/checkout@v3\n      - name: Prepare enviroment\n        run: |\n          python -m pip install --upgrade pip\n          pip install jina\n          pip install --pre docarray\n          pip install pytest pytest-html pytest-cov pytest-mock pytest-repeat pytest-custom-exit-code pytest-timeout pytest-reraise\n      - name: Build wheel\n        uses: pypa/cibuildwheel@v2.10.2\n        with:\n          package-dir: ./\n        env:\n          CIBW_ENVIRONMENT: >\n            STAN_BACKEND=\"${{ env.STAN_BACKEND }}\"\n          CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014\n          CIBW_BUILD: ${{ matrix.cpython-version }}\n          CIBW_SKIP: \"*musllinux*\"\n          CIBW_ARCHS: ${{ matrix.cibw_arch }}\n          CIBW_BUILD_FRONTEND: build\n      - name: Install annlite unix\n        if: ${{ matrix.os == 'ubuntu-latest' }}\n        run: |\n          pip install wheelhouse/*linux*.whl\n      - name: Install annlite win\n        if: ${{ matrix.os == 'windows-latest'}}\n        run: |\n          pip install --pre --find-links=wheelhouse/ annlite\n      - name: Test unix\n        id: test_unix\n        if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' }}\n        run: |\n          cd ..\n          mv annlite/tests/ ./\n          pytest --suppress-no-test-exit-code --cov=annlite --cov-report=xml \\\n          -v -s -m \"not gpu\" ${{ matrix.test-path }}\n          echo \"::set-output name=codecov_flag::annlite\"\n        timeout-minutes: 30\n      - name: Test win\n        id: test_win\n        if: ${{ matrix.os == 'windows-latest'}}\n        env:\n          PYTHONIOENCODING: 'utf-8'\n        run: |\n          cd ..\n          move annlite/tests/ ./\n          cd tests/\n          pytest -v -s -m \"not gpu\" -k \"test\"\n          echo \"::set-output name=codecov_flag::annlite\"\n        timeout-minutes: 30\n      - name: Check codecov file\n        id: check_files\n        uses: andstor/file-existence-action@v1\n        with:\n          files: \"coverage.xml\"\n      - name: Upload coverage from test to Codecov\n        uses: codecov/codecov-action@v2\n        if: steps.check_files.outputs.files_exists == 'true' && ${{ matrix.python-version }} == '3.7'\n        with:\n          file: coverage.xml\n          flags: ${{ steps.test.outputs.codecov_flag }}\n          fail_ci_if_error: false\n          token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos\n\n  # just for blocking the merge until all parallel core-test are successful\n  success-all-test:\n    needs: [core-test]\n    if: always()\n    runs-on: ubuntu-latest\n    steps:\n      - uses: technote-space/workflow-conclusion-action@v2\n      - name: Check Failure\n        if: env.WORKFLOW_CONCLUSION == 'failure'\n        run: exit 1\n      - name: Success\n        if: ${{ success() }}\n        run: echo \"All Done\"\n"
  },
  {
    "path": ".github/workflows/force-release.yml",
    "content": "name: Manual Release\n\non:\n  workflow_dispatch:\n    inputs:\n      release_token:\n        description: 'Your release token'\n        required: true\n      release_reason:\n        description: 'Short reason for this manual release'\n        required: true\n\njobs:\n  token-check:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo \"success!\"\n        if: \"${{ github.event.inputs.release_token }} == ${{ env.release_token }}\"\n        env:\n          release_token: ${{ secrets.ANNLITE_RELEASE_TOKEN }}\n\n  build-wheels:\n    needs: [token-check]\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ ubuntu-latest, windows-latest, macos-latest ]\n        cibw_arch: [ \"auto64\" ]\n        python-version: [ [ '3.7', \"cp37-*\" ], [ '3.8', \"cp38-*\" ], [ '3.9', \"cp39-*\" ], [ '3.10', \"cp310-*\" ] ]\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 100\n      - name: Set up Python ${{ matrix.python-version[0] }}\n        uses: actions/setup-python@v4\n        with:\n          python-version: ${{ matrix.python-version[0] }}\n      - name: Build sdist\n        if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version[0] == '3.7' }}\n        run: |\n          pip install build\n          python -m build --sdist\n      - name: Build wheels\n        uses: pypa/cibuildwheel@v2.10.2\n        with:\n          package-dir: ./\n        env:\n          CIBW_ENVIRONMENT: >\n            STAN_BACKEND=\"${{ env.STAN_BACKEND }}\"\n          CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014\n          CIBW_BUILD: ${{ matrix.python-version[1] }}\n          CIBW_SKIP: \"*musllinux*\"\n          CIBW_ARCHS: ${{ matrix.cibw_arch }}\n          # CIBW_ARCH_MACOS: x86_64 arm64\n          CIBW_BUILD_FRONTEND: build\n      - uses: actions/upload-artifact@v3\n        with:\n          path: |\n            ./wheelhouse/*.whl\n            ./dist/*.tar.gz\n\n  regular-release:\n    needs: build-wheels\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          token: ${{ secrets.JINA_DEV_BOT }}\n          fetch-depth: 100  # means max contribute history is limited to 100 lines\n#          submodules: true\n      - uses: actions/setup-python@v2\n        with:\n          python-version: 3.7\n      - uses: actions/download-artifact@v3\n        with:\n          name: artifact\n      - run: |\n          git fetch --depth=1 origin +refs/tags/*:refs/tags/*\n          npm install git-release-notes\n          pip install twine wheel\n          ./scripts/release.sh final \"${{ github.event.inputs.release_reason }}\" \"${{github.actor}}\"\n        env:\n          TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}\n          TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}\n          JINA_SLACK_WEBHOOK: ${{ secrets.JINA_SLACK_WEBHOOK }}\n      - if: failure()\n        run: echo \"nothing to release\"\n      - name: bumping master version\n        uses: ad-m/github-push-action@v0.6.0\n        with:\n          github_token: ${{ secrets.JINA_DEV_BOT }}\n          tags: true\n          branch: main\n"
  },
  {
    "path": ".github/workflows/tag.yml",
    "content": "name: Release CD\n\non:\n  push:\n    tags:\n      - \"v*\"  # push to version tags trigger the build\n\n#on:\n#  push:\n#    branches-ignore:\n#      - '**'  # temporally disable this action\n\njobs:\n  create-release:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v2\n        with:\n          ref: 'main'\n      - uses: actions/setup-python@v2\n        with:\n          python-version: 3.7\n      - run: |\n          python scripts/get-last-release-note.py\n      - name: Create Release\n        id: create_release\n        uses: actions/create-release@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token\n        with:\n          tag_name: ${{ github.ref }}\n          release_name: 💫 Patch ${{ github.ref }}\n          body_path: 'tmp.md'\n          draft: false\n          prerelease: false\n"
  },
  {
    "path": ".gitignore",
    "content": "# Initially taken from Github's Python gitignore file\n# local dev\n.vscode\nbindings/pq_bindings.cpp\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\ndocs/.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n.idea/\ntoy*.py\n.DS_Store\npost/\ntoy*.ipynb\ndata/\n*.c\n.nes_cache\ntoy*.yml\n*.tmp\n\n/junit/\n/tests/junit/\n/docs/chapters/proto/docs.md\n\n# IntelliJ IDEA\n*.iml\n.idea\n\n# Rust\n/target\nCargo.lock\n\ntoy*.py\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  #- repo: https://github.com/terrencepreilly/darglint\n  #  rev: v1.5.8\n  #  hooks:\n  #  - id: darglint\n  #    files: annlite/\n  #    exclude: docs/\n  #    args:\n  #    - --message-template={path}:{line} {msg_id} {msg}\n  #    - -s=sphinx\n  #    - -z=full\n  #    - -v=2\n  #- repo: https://github.com/pycqa/pydocstyle\n  #  rev: 5.1.1  # pick a git hash / tag to point to\n  #  hooks:\n  #  -   id: pydocstyle\n  #      files: annlite/\n  #      exclude: docs/\n  #      args:\n  #      - --select=D101,D102,D103\n  - repo: https://github.com/timothycrosley/isort\n    rev: 5.12.0\n    hooks:\n      - id: isort\n        args: [\"--profile\", \"black\"]\n  - repo: https://github.com/psf/black\n    rev: 22.3.0\n    hooks:\n      - id: black\n        types: [python]\n        exclude: docs/\n        args:\n          - -S\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.0.1\n    hooks:\n      - id: trailing-whitespace\n      - id: check-yaml\n      - id: end-of-file-fixer\n      - id: requirements-txt-fixer\n      - id: double-quote-string-fixer\n      - id: check-merge-conflict\n      - id: fix-encoding-pragma\n        args: [\"--remove\"]\n      - id: mixed-line-ending\n        args: [\"--fix=lf\"]\n  - repo: https://github.com/pre-commit/mirrors-clang-format\n    rev: \"v14.0.3\"\n    hooks:\n      - id: clang-format\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<a name=release-note-0-3-0></a>\n## Release Note (`0.3.0`)\n\n> Release time: 2022-03-04 11:49:06\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n numb3r3,  felix-wang,  David Buchaca Prats,  🙇\n\n\n### 🆕 New Features\n\n - [[```e4afbe17```](https://github.com/jina-ai/docarray/commit/e4afbe17c724f48de19bfbe6d6c127e62a8bcd5a)] __-__ add cd workflow (*numb3r3*)\n\n### 🐞 Bug fixes\n\n - [[```2aeb5377```](https://github.com/jina-ai/docarray/commit/2aeb537779df6649fab143affa5c5decf8b2e364)] __-__ format (*numb3r3*)\n - [[```27ab16c2```](https://github.com/jina-ai/docarray/commit/27ab16c2622606e7bf77af5286620f0cdec4abb0)] __-__ build dep (*numb3r3*)\n - [[```7887ac95```](https://github.com/jina-ai/docarray/commit/7887ac95a7954c783264151cd26ae47319a47baa)] __-__ turn on upload pypi (#109) (*felix-wang*)\n - [[```2e4739ac```](https://github.com/jina-ai/docarray/commit/2e4739ac2f19e78bc23889ee2a1baef79be65e5d)] __-__ version (*numb3r3*)\n - [[```293a0dbf```](https://github.com/jina-ai/docarray/commit/293a0dbfe8b063c66ec4b8b710f44eb1c8ff4041)] __-__ release script (*numb3r3*)\n - [[```e77c95d9```](https://github.com/jina-ai/docarray/commit/e77c95d97fe54bb409ce39931de86fb45fdc6f41)] __-__ cov (*numb3r3*)\n - [[```287de379```](https://github.com/jina-ai/docarray/commit/287de379686fc4eb91afd99f32a85e3dcbc6bf27)] __-__ install in cd (*numb3r3*)\n - [[```88b8e0b6```](https://github.com/jina-ai/docarray/commit/88b8e0b6c033f33d76e5ec3e7e95f3de14d7ed70)] __-__ pip install in ci (*numb3r3*)\n - [[```348e0444```](https://github.com/jina-ai/docarray/commit/348e044463039c1a4ed60c43c1f83353378a86f7)] __-__ release (*numb3r3*)\n - [[```beb35f29```](https://github.com/jina-ai/docarray/commit/beb35f29c1f695728e7c43b235abb97a71f797ca)] __-__ setup pytest (*numb3r3*)\n - [[```e9ea4b89```](https://github.com/jina-ai/docarray/commit/e9ea4b8942d48034a7a471e2b91a6b542913f7c9)] __-__ workflow yml (*numb3r3*)\n - [[```5f0e21ec```](https://github.com/jina-ai/docarray/commit/5f0e21ec85a09a1dabc618841eb9fcc4c56c34bb)] __-__ ci yml (*numb3r3*)\n - [[```db58e283```](https://github.com/jina-ai/docarray/commit/db58e2835f758c94ec73fa3ccc795c286fdb4e86)] __-__ setup.py (*numb3r3*)\n - [[```bdc1c7a2```](https://github.com/jina-ai/docarray/commit/bdc1c7a276086b702c807112d0603c245476944a)] __-__ __cicd__: add scripts (*numb3r3*)\n\n### 🧼 Code Refactoring\n\n - [[```9c693adb```](https://github.com/jina-ai/docarray/commit/9c693adb438742b633d04f554e442ceff9da923d)] __-__ remove pqlite namings (*David Buchaca Prats*)\n - [[```793f4711```](https://github.com/jina-ai/docarray/commit/793f4711c42d3a717e9e60e3548c222f1ecc7de2)] __-__ rename project (*numb3r3*)\n\n### 🍹 Other Improvements\n\n - [[```f0c6d809```](https://github.com/jina-ai/docarray/commit/f0c6d809d47a6142f5ccf40c7cc7cf24045ad2e5)] __-__ bump version (*numb3r3*)\n\n<a name=release-note-0-3-1></a>\n## Release Note (`0.3.1`)\n\n> Release time: 2022-03-04 16:29:52\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```44298038```](https://github.com/jina-ai/docarray/commit/44298038e4e79f3dd0c3532ba6ad5b64d1a35caf)] __-__ add tag release workflow (#110) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```d15289f9```](https://github.com/jina-ai/docarray/commit/d15289f9912f158c7b9a7ec7d5f58188ecb19cf2)] __-__ __version__: the next version will be 0.3.1 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-2></a>\n## Release Note (`0.3.2`)\n\n> Release time: 2022-06-09 10:04:30\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n David Buchaca Prats,  Gustavo Ye,  felix-wang,  Han Xiao,  numb3r3,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```a7804baf```](https://github.com/jina-ai/docarray/commit/a7804bafc90064f929afff62192f19e44406b5d1)] __-__ add filter method (#121) (*David Buchaca Prats*)\n\n### 🐞 Bug fixes\n\n - [[```9448fcab```](https://github.com/jina-ai/docarray/commit/9448fcab060efe5b59108f3cac183aeb432a81d6)] __-__ only apply black with changed py files (#118) (*felix-wang*)\n - [[```e4e706e3```](https://github.com/jina-ai/docarray/commit/e4e706e313ba5cbfb7083a5dea9e75b8d2813394)] __-__ upgrade traverse path (#113) (*felix-wang*)\n - [[```a0fc7cac```](https://github.com/jina-ai/docarray/commit/a0fc7cac7722d108143f6590c3435c77376f76bd)] __-__ tag yml (*numb3r3*)\n\n### 🍹 Other Improvements\n\n - [[```2ce1ec22```](https://github.com/jina-ai/docarray/commit/2ce1ec2283b381f5153ea60141a6bb474bbf0f0c)] __-__ __cpp/h__: clang-format (#117) (*Gustavo Ye*)\n - [[```a6168400```](https://github.com/jina-ai/docarray/commit/a61684000af4518aafa41d1d9dea47766bedf247)] __-__ update readme (*Han Xiao*)\n - [[```aec33c75```](https://github.com/jina-ai/docarray/commit/aec33c75b8ce883e044a828fbae7142f71dbb05a)] __-__ __version__: the next version will be 0.3.2 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-3></a>\n## Release Note (`0.3.3`)\n\n> Release time: 2022-07-04 13:57:38\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Gustavo Ye,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```e240128f```](https://github.com/jina-ai/docarray/commit/e240128f403bde04bc21e1ac5ce3baa5507db687)] __-__ proto version bump (#126) (*felix-wang*)\n - [[```f62809b5```](https://github.com/jina-ai/docarray/commit/f62809b5d90f4fca1762c6a1c13beee40e972212)] __-__ change input type to `data_t` (#123) (*Gustavo Ye*)\n\n### 🍹 Other Improvements\n\n - [[```f7f7b751```](https://github.com/jina-ai/docarray/commit/f7f7b75104a28f1860d3cdc94c87f526e742db51)] __-__ __version__: the next version will be 0.3.3 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-4></a>\n## Release Note (`0.3.4`)\n\n> Release time: 2022-07-20 07:12:22\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```1a9de05c```](https://github.com/jina-ai/docarray/commit/1a9de05cdcbb861cd036b2ed9a3a66fa84e707f9)] __-__ copy executor.py to annlite folder (#129) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```5225e6cb```](https://github.com/jina-ai/docarray/commit/5225e6cbec7df3fbf4dbd7ab404d34bb7d899a29)] __-__ __version__: the next version will be 0.3.4 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-5></a>\n## Release Note (`0.3.5`)\n\n> Release time: 2022-07-29 08:17:02\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```534eed81```](https://github.com/jina-ai/docarray/commit/534eed810e6b56238f0b71cc4123783236eac7fd)] __-__ support load and save hnsw (#133) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```1f76fb5d```](https://github.com/jina-ai/docarray/commit/1f76fb5dedc79d331dc655d6409718457c6c05d7)] __-__ Feat task queue (#131) (*Jie Fu*)\n - [[```103b5bf3```](https://github.com/jina-ai/docarray/commit/103b5bf31b59ca041d7ca70b8e0442421bcdf279)] __-__ __version__: the next version will be 0.3.5 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-6></a>\n## Release Note (`0.3.6`)\n\n> Release time: 2022-08-23 10:45:36\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jie Fu,  Gustavo Ye,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```690098be```](https://github.com/jina-ai/docarray/commit/690098be576fd5d6149e0502b9d2bf100b726f27)] __-__ add pq support for hnsw searching (#122) (*Gustavo Ye*)\n - [[```ad5a5fe3```](https://github.com/jina-ai/docarray/commit/ad5a5fe39293f771b9945102b45f05bebfaf3ad6)] __-__ indexer dumploader (#137) (*felix-wang*)\n - [[```29019e3d```](https://github.com/jina-ai/docarray/commit/29019e3da94d0a6025c91919a8978966f073b608)] __-__ integrate annlite with projector (#136) (*Jie Fu*)\n - [[```14c95986```](https://github.com/jina-ai/docarray/commit/14c9598602741a5558c02a98bd123b34cddc32b8)] __-__ implement pca (#135) (*Jie Fu*)\n\n### 🐞 Bug fixes\n\n - [[```9f74de8f```](https://github.com/jina-ai/docarray/commit/9f74de8f34bc51ab740a05e9bfabf0be4ec77b87)] __-__ reload duplicate storage (#143) (*felix-wang*)\n - [[```7010d778```](https://github.com/jina-ai/docarray/commit/7010d77869dfd2a44ebaeb4a697bbac3e01d6970)] __-__ fix update/delete (#140) (*Jie Fu*)\n - [[```8a05887d```](https://github.com/jina-ai/docarray/commit/8a05887dc68d4219190de8767c98fc5a1740e3a2)] __-__ composite pca and pq (#139) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```157a1a9c```](https://github.com/jina-ai/docarray/commit/157a1a9c189f73f5757d2ec60c4427e41cd130bd)] __-__ __version__: the next version will be 0.3.6 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-7></a>\n## Release Note (`0.3.7`)\n\n> Release time: 2022-08-26 04:48:47\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```01cf7a89```](https://github.com/jina-ai/docarray/commit/01cf7a8960f27abcbc414faf2d274e0d8da470db)] __-__ fix np.int64 issue (#146) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```b602acce```](https://github.com/jina-ai/docarray/commit/b602acce8f7f2ac085f9cbccae6fadeee1ebcb85)] __-__ __version__: the next version will be 0.3.7 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-8></a>\n## Release Note (`0.3.8`)\n\n> Release time: 2022-08-26 11:12:19\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```a5eaa53e```](https://github.com/jina-ai/docarray/commit/a5eaa53e84aa374e061ca0bfddc7797fe369f8ec)] __-__ insert when not found at update (#151) (*felix-wang*)\n - [[```18af2d4c```](https://github.com/jina-ai/docarray/commit/18af2d4c42ced2ca9d393456ba9722953edce383)] __-__ normalize training data for cosine metric (#150) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```f0fb1c4b```](https://github.com/jina-ai/docarray/commit/f0fb1c4b37c89fea31fc24fb47b60181f0cd3218)] __-__ __version__: the next version will be 0.3.8 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-9></a>\n## Release Note (`0.3.9`)\n\n> Release time: 2022-08-31 03:46:53\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Gustavo Ye,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```27589456```](https://github.com/jina-ai/docarray/commit/27589456fe72c3e9a1fe7d6c8d198e0e71fd9a89)] __-__ reload indexer (#154) (*felix-wang*)\n - [[```cdbe7256```](https://github.com/jina-ai/docarray/commit/cdbe725691cf5cc19db5df250d6d9d384bc94437)] __-__ __py__: roll back the original decorator (#142) (*Gustavo Ye*)\n\n### 🍹 Other Improvements\n\n - [[```f34062b2```](https://github.com/jina-ai/docarray/commit/f34062b2249a3fce8cb51dc9e8b8497d33a54a2f)] __-__ __version__: the next version will be 0.3.9 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-10></a>\n## Release Note (`0.3.10`)\n\n> Release time: 2022-09-06 09:40:07\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  numb3r3,  Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```bc7f5ae2```](https://github.com/jina-ai/docarray/commit/bc7f5ae2e8e83cef919052a9d8a3a954a1cd813f)] __-__ allow columns as dict format (#160) (*felix-wang*)\n - [[```25c3ec3d```](https://github.com/jina-ai/docarray/commit/25c3ec3d616a9ece53c710d734ba71d0eb6fc19c)] __-__ thread safe index and search (#157) (*felix-wang*)\n - [[```d235cb83```](https://github.com/jina-ai/docarray/commit/d235cb83a2d8399f1120b3ebf6e54392ecb37efa)] __-__ executor unittest (#156) (*felix-wang*)\n\n### 🏁 Unit Test and CICD\n\n - [[```e6543a28```](https://github.com/jina-ai/docarray/commit/e6543a28894aad5481d1a6a5bbd8f070ca979a90)] __-__ add docarray tests (#153) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```6a774648```](https://github.com/jina-ai/docarray/commit/6a774648d772ad98f42641c6094192bf4e7d1877)] __-__ update logo (*numb3r3*)\n - [[```33fd66be```](https://github.com/jina-ai/docarray/commit/33fd66be1135ebe63f2a1e1366196c8712e2bc63)] __-__ update readme (*numb3r3*)\n - [[```e1ad3a55```](https://github.com/jina-ai/docarray/commit/e1ad3a55d4732a115e6a925075b7b5ed768cd9f8)] __-__ update log (*numb3r3*)\n - [[```c2630257```](https://github.com/jina-ai/docarray/commit/c26302572de4e755d64ffee71b017e798f790e9e)] __-__  update readme (#149) (*felix-wang*)\n - [[```c1fd8cfe```](https://github.com/jina-ai/docarray/commit/c1fd8cfe746d94de2ebef843f6e13384f266773b)] __-__ __version__: the next version will be 0.3.10 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-11></a>\n## Release Note (`0.3.11`)\n\n> Release time: 2022-09-08 06:17:25\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```0b435eb6```](https://github.com/jina-ai/docarray/commit/0b435eb6c5a737d370b389ba3c2395796febe95f)] __-__ bump pybind11 (#162) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```9c43f02c```](https://github.com/jina-ai/docarray/commit/9c43f02c5ec9face32ee58858b8ab25d65edbf12)] __-__ __version__: the next version will be 0.3.11 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-12></a>\n## Release Note (`0.3.12`)\n\n> Release time: 2022-09-19 05:15:49\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  YangXiuyu,  numb3r3,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```4eb61d3d```](https://github.com/jina-ai/docarray/commit/4eb61d3dea5a86d5212dafaaa0c1ecd87abc399a)] __-__ add limit and offset for filtering (#167) (*felix-wang*)\n\n### 📗 Documentation\n\n - [[```6ecfa833```](https://github.com/jina-ai/docarray/commit/6ecfa833e3d0f69a1a1b4992724753c89fa8d96f)] __-__ fix typo (#168) (*YangXiuyu*)\n\n### 🍹 Other Improvements\n\n - [[```6902a8b1```](https://github.com/jina-ai/docarray/commit/6902a8b15763e73fd1cd90827ccfdc758f2fce32)] __-__ add description about hnsw parameters (#169) (*felix-wang*)\n - [[```aec2d605```](https://github.com/jina-ai/docarray/commit/aec2d605d43394638168282c78e13f7f2150200a)] __-__ update readme (*numb3r3*)\n - [[```95541bad```](https://github.com/jina-ai/docarray/commit/95541bad119dd2bb03e9894f32f57578cd3d8a7a)] __-__ __version__: the next version will be 0.3.12 (*Jina Dev Bot*)\n\n<a name=release-note-0-3-13></a>\n## Release Note (`0.3.13`)\n\n> Release time: 2022-09-26 02:04:59\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jie Fu,  numb3r3,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```f98a8336```](https://github.com/jina-ai/docarray/commit/f98a83368e9fc81d82abb2be9c8a6569ee45e177)] __-__ change sqlite in_memory to false (#173) (*Jie Fu*)\n\n### 🐞 Bug fixes\n\n - [[```b5f8694f```](https://github.com/jina-ai/docarray/commit/b5f8694fb5c33af15cc9b4a140a0165bf3aa01f4)] __-__ __hnswlib__: cannot find sufficient data with filtering (#176) (*felix-wang*)\n - [[```0aa07863```](https://github.com/jina-ai/docarray/commit/0aa078633d7b6b850a3ea0ffe651dbad06240cc0)] __-__ __ci__: unittest (#175) (*felix-wang*)\n\n### 🧼 Code Refactoring\n\n - [[```896c5006```](https://github.com/jina-ai/docarray/commit/896c5006a6cac5f6b144bcb452ec5b2169ca4c88)] __-__ use rockdb as the storage backend (#171) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```66257b41```](https://github.com/jina-ai/docarray/commit/66257b4147836a8e8b7c17dd12537143509e78a2)] __-__ Revert &#34;refactor: use rockdb as the storage backend (#171)&#34; (*numb3r3*)\n - [[```e56aae75```](https://github.com/jina-ai/docarray/commit/e56aae757e65e5f8294a15b6ba998e00bda97e1a)] __-__ __version__: the next version will be 0.3.13 (*Jina Dev Bot*)\n\n<a name=release-note-0-4-0></a>\n## Release Note (`0.4.0`)\n\n> Release time: 2022-10-24 09:53:20\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n numb3r3,  Jie Fu,  YangXiuyu,  felix-wang,  Ziniu Yu,  Gustavo Ye,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```1cd07f2c```](https://github.com/jina-ai/docarray/commit/1cd07f2c33e4f1f4e756baab4a6cd0a1452996cf)] __-__ hub as remote storage (#177) (*YangXiuyu*)\n - [[```90db6006```](https://github.com/jina-ai/docarray/commit/90db60067205d8e64a63c0c7eb4e782ac04d1033)] __-__ add flag for close (#191) (*Jie Fu*)\n - [[```3b782900```](https://github.com/jina-ai/docarray/commit/3b7829004edfb4f6cf4b77048a747a0d338c21b3)] __-__ cibuildwheels (#186) (*YangXiuyu*)\n\n### 🐞 Bug fixes\n\n - [[```8c807937```](https://github.com/jina-ai/docarray/commit/8c80793733e96680d2ca846371ebc3c684f1d5d7)] __-__ restore when initializing annlite (#201) (*Jie Fu*)\n - [[```98f94d0f```](https://github.com/jina-ai/docarray/commit/98f94d0f4ff1bd0b1bc1b99d828d6fd21932dc2b)] __-__ set is_train=true after loading pca (#199) (*Jie Fu*)\n - [[```0b9bb413```](https://github.com/jina-ai/docarray/commit/0b9bb413abe631ea07bd7358160060b16cafa827)] __-__ unittest (#198) (*YangXiuyu*)\n - [[```0d1d5a2e```](https://github.com/jina-ai/docarray/commit/0d1d5a2e58eb043397f24a1d6efcee07c22e2cb1)] __-__ pre-release (*numb3r3*)\n - [[```fdcf3818```](https://github.com/jina-ai/docarray/commit/fdcf3818f1b8a2b11df96f4c6c72e972bab91238)] __-__ cd release version (#197) (*YangXiuyu*)\n - [[```f4bbc495```](https://github.com/jina-ai/docarray/commit/f4bbc49529a2ac4174bbb0cc7c88ab8666bd034c)] __-__ clean codes (*numb3r3*)\n - [[```6bedafe4```](https://github.com/jina-ai/docarray/commit/6bedafe4c49ce8c4831b4cc01c1b8702924e4b8f)] __-__ test-paths (*numb3r3*)\n - [[```beb79b23```](https://github.com/jina-ai/docarray/commit/beb79b23a1b1412bad402410a0b90393f0e535b6)] __-__ __cd__: combine build and test (*numb3r3*)\n - [[```19652ed0```](https://github.com/jina-ai/docarray/commit/19652ed0b6ef0ffc9dc1c3d2dc2640075e4d6d13)] __-__ cd workflow (#196) (*YangXiuyu*)\n - [[```4fe2b3be```](https://github.com/jina-ai/docarray/commit/4fe2b3be51ed0b571c6f0b9c9f20a68c18b7486c)] __-__ cd workflow (#195) (*YangXiuyu*)\n - [[```4fdacde2```](https://github.com/jina-ai/docarray/commit/4fdacde2a22a5e6fb0edc15bb6f77bf4d8f25834)] __-__ cd workflow (#193) (*YangXiuyu*)\n - [[```494fbfcb```](https://github.com/jina-ai/docarray/commit/494fbfcbb96257168542f56ec617a9dc9082c084)] __-__ cd release (#192) (*felix-wang*)\n - [[```d2803ce0```](https://github.com/jina-ai/docarray/commit/d2803ce00e41a69470a3001e4677927b407c3282)] __-__ cd workflow (#190) (*felix-wang*)\n - [[```fe9db3d9```](https://github.com/jina-ai/docarray/commit/fe9db3d9f7e04cbad96832409dd1d3159195c060)] __-__ build on apple silicon (#188) (*Ziniu Yu*)\n - [[```bb402ae9```](https://github.com/jina-ai/docarray/commit/bb402ae9674a8410686b8dde3aea82f1b86fc10b)] __-__ __bindings__: build on windows (#183) (*YangXiuyu*)\n - [[```efc18f80```](https://github.com/jina-ai/docarray/commit/efc18f80228ef2371c19fbb205cabb94afd47385)] __-__ update executor parameter (#180) (*YangXiuyu*)\n - [[```f142089a```](https://github.com/jina-ai/docarray/commit/f142089a419bba3d7e23b2c34b16455ddbcb805d)] __-__ executor tests (#179) (*felix-wang*)\n - [[```79e171d4```](https://github.com/jina-ai/docarray/commit/79e171d411f31dad7231bce1682ac78ff7b3e1b2)] __-__ __annliteindexer__: annlite executor integration (#170) (*YangXiuyu*)\n\n### 🧼 Code Refactoring\n\n - [[```e8c59907```](https://github.com/jina-ai/docarray/commit/e8c59907540bada5282a413b43d64f11596678d3)] __-__ use rocksdb as the docs storage engine (#178) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```e6dfcd2a```](https://github.com/jina-ai/docarray/commit/e6dfcd2a7ff0fdd149dd606487f7aa0107775c63)] __-__ bump to v0.4.0 (*numb3r3*)\n - [[```6ce03fd0```](https://github.com/jina-ai/docarray/commit/6ce03fd04e5bb63da3c3d2cf1c620ac77daf6c2a)] __-__ release (*numb3r3*)\n - [[```02857ec4```](https://github.com/jina-ai/docarray/commit/02857ec4103e32139f695adac4f7a40c4e65c67a)] __-__ Add pq dist table support (#158) (*Gustavo Ye*)\n - [[```ff54290b```](https://github.com/jina-ai/docarray/commit/ff54290bfb19d07b953aa00d5d52cb1e8f805d3a)] __-__ __version__: the next version will be 0.3.14 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-0></a>\n## Release Note (`0.5.0`)\n\n> Release time: 2022-10-26 07:12:58\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n numb3r3,  felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```de5273ab```](https://github.com/jina-ai/docarray/commit/de5273ab7cf81ba26bb4307cd66f3fc50974b8ef)] __-__ undo wheels uploading (#203) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```5faf1dfa```](https://github.com/jina-ai/docarray/commit/5faf1dfa3326213c3b9e20de0d8b7aca4dbdf6ba)] __-__ regular release (*numb3r3*)\n - [[```95e51f26```](https://github.com/jina-ai/docarray/commit/95e51f269ac130b9d54b004734ff68afc07a9cf1)] __-__ __version__: the next version will be 0.4.1 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-1></a>\n## Release Note (`0.5.1`)\n\n> Release time: 2022-11-03 03:29:55\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n numb3r3,  Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🆕 New Features\n\n - [[```dd08f6f2```](https://github.com/jina-ai/docarray/commit/dd08f6f28008aaf020cb972f45e154afaf99cb28)] __-__ add file splitter and merger to support big file transfer to hubble (#202) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```83e45001```](https://github.com/jina-ai/docarray/commit/83e4500180e98bb967ab2ec17ca202c1702fb411)] __-__ bump 0.5.1 (*numb3r3*)\n - [[```557c624b```](https://github.com/jina-ai/docarray/commit/557c624b3419b8bf70386e66a75d1958ef427738)] __-__ fix force release (*numb3r3*)\n - [[```77312214```](https://github.com/jina-ai/docarray/commit/773122144a9ec39663829c64090d6116585fb6e1)] __-__ __version__: the next version will be 0.5.1 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-2></a>\n## Release Note (`0.5.2`)\n\n> Release time: 2022-11-03 07:10:10\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```3f886f10```](https://github.com/jina-ai/docarray/commit/3f886f10d17195913cea839f3e4ac8631426ad5c)] __-__ restore from latest snapshot (#204) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```2462afa4```](https://github.com/jina-ai/docarray/commit/2462afa4451c66603cc1e25bf755c97a97b7eec1)] __-__ __version__: the next version will be 0.5.2 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-4></a>\n## Release Note (`0.5.4`)\n\n> Release time: 2022-11-04 04:27:28\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```f2ce3a9a```](https://github.com/jina-ai/docarray/commit/f2ce3a9a4b35a71a492fbe63203114e0d7fa6b52)] __-__ bump rocksdict (#206) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```c84635c9```](https://github.com/jina-ai/docarray/commit/c84635c9854b7b3c2b5718941979720b673ed7eb)] __-__ __version__: the next version will be 0.5.3 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-5></a>\n## Release Note (`0.5.5`)\n\n> Release time: 2022-12-22 03:27:38\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```61e0a57f```](https://github.com/jina-ai/docarray/commit/61e0a57f901b087fbe6cab16aefb1d46d5fd54a2)] __-__ fix offset factory (#208) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```f6fdc9d3```](https://github.com/jina-ai/docarray/commit/f6fdc9d34fd8e8de47e7ac68e6813956c2aeebfe)] __-__ __version__: the next version will be 0.5.5 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-6></a>\n## Release Note (`0.5.6`)\n\n> Release time: 2023-02-08 09:06:21\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```906ed7d4```](https://github.com/jina-ai/docarray/commit/906ed7d4159527e8b5f00cdc9106433925f04a43)] __-__ save and load offset2ids for list_like feature (#210) (*Jie Fu*)\n\n### 🏁 Unit Test and CICD\n\n - [[```a2519f0f```](https://github.com/jina-ai/docarray/commit/a2519f0f323d0c3cb2d68ad1cd3eeed4beb40ce6)] __-__ add test for offset2ids saving and loading (#212) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```68215807```](https://github.com/jina-ai/docarray/commit/68215807953eed366e1d6f48fbbbbc1abebf13ff)] __-__ __version__: the next version will be 0.5.6 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-7></a>\n## Release Note (`0.5.7`)\n\n> Release time: 2023-02-21 12:34:55\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```4a66d0c0```](https://github.com/jina-ai/docarray/commit/4a66d0c06e7497cb94cd18da505a2e309594b20c)] __-__ remove delete tags for cell_table and meta_table (#215) (*Jie Fu*)\n\n### 🍹 Other Improvements\n\n - [[```eee752fe```](https://github.com/jina-ai/docarray/commit/eee752fe091dc5b5b519bb2aa866cb7d5dc48822)] __-__ __version__: the next version will be 0.5.7 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-8></a>\n## Release Note (`0.5.8`)\n\n> Release time: 2023-03-24 11:13:35\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  YangXiuyu,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```bfeb7e94```](https://github.com/jina-ai/docarray/commit/bfeb7e94c879db263dc4931b54b6b87dac09df81)] __-__ delete and update (#222) (*felix-wang*)\n - [[```7783b30f```](https://github.com/jina-ai/docarray/commit/7783b30fd55a10bd2dd0c75dc0451f0fe1834927)] __-__ __hnsw__: bump hnswlib to v0.6.2 (#185) (*YangXiuyu*)\n\n### 🍹 Other Improvements\n\n - [[```4c145ddd```](https://github.com/jina-ai/docarray/commit/4c145ddd19abb4caec479941d1c0ffb03c4cfcf3)] __-__ __version__: the next version will be 0.5.8 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-9></a>\n## Release Note (`0.5.9`)\n\n> Release time: 2023-04-07 04:16:28\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n felix-wang,  Jina Dev Bot,  🙇\n\n\n### 🐞 Bug fixes\n\n - [[```ab807ff6```](https://github.com/jina-ai/docarray/commit/ab807ff69e533ec90e0b0f6c782749267324d177)] __-__ bump rocksdb &gt;= 0.3.9 (#225) (*felix-wang*)\n\n### 🍹 Other Improvements\n\n - [[```3668c3fa```](https://github.com/jina-ai/docarray/commit/3668c3fa6f7a2d2af39549987637e11e385a75bb)] __-__ __version__: the next version will be 0.5.9 (*Jina Dev Bot*)\n\n<a name=release-note-0-5-10></a>\n## Release Note (`0.5.10`)\n\n> Release time: 2023-04-19 03:12:50\n\n\n\n🙇 We'd like to thank all contributors for this new release! In particular,\n Jie Fu,  Jina Dev Bot,  🙇\n\n\n### 🍹 Other Improvements\n\n - [[```d55d544e```](https://github.com/jina-ai/docarray/commit/d55d544e876653bb47e79e2dd00f859c3ef00ffa)] __-__ bumping docarray (#227) (*Jie Fu*)\n - [[```86057c69```](https://github.com/jina-ai/docarray/commit/86057c69401e2b6d63822be2c3c3a0f63d4661a6)] __-__ __version__: the next version will be 0.5.10 (*Jina Dev Bot*)\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include setup.py\ninclude requirements.txt\ninclude pyproject.toml\nglobal-include *.pyx\nrecursive-include include/hnswlib/ *\n"
  },
  {
    "path": "Makefile",
    "content": "pypi: dist\n\ttwine upload dist/*\n\ndist:\n\trm -rf dist/*\n\tpip install build\n\tpython -m build --sdist\n\ntest:\n\tpython -m unittest discover --start-directory tests --pattern \"*_test*.py\"\n\nclean:\n\trm -rf *.egg-info build dist tmp var tests/__pycache__ annlite/*.so\n\n.PHONY: dist\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n<br>\n<br>\n<br>\n<img src=\"https://github.com/jina-ai/annlite/blob/main/.github/assets/logo.svg?raw=true\" alt=\"AnnLite logo: A fast and efficient ann libray\" width=\"200px\">\n<br>\n<br>\n<b>A fast embedded library for approximate nearest neighbor search</b>\n</p>\n\n<p align=center>\n<a href=\"hhttps://github.com/jina-ai/annlite\"><img alt=\"GitHub\" src=\"https://img.shields.io/github/license/jina-ai/annlite?style=flat-square\"></a>\n<a href=\"https://pypi.org/project/annlite/\"><img alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/annlite?label=Release&style=flat-square\"></a>\n<a href=\"https://codecov.io/gh/jina-ai/annlite\"><img alt=\"Codecov branch\" src=\"https://img.shields.io/codecov/c/github/jina-ai/annlite/main?logo=Codecov&logoColor=white&style=flat-square\"></a>\n<a href=\"https://slack.jina.ai\"><img src=\"https://img.shields.io/badge/Slack-3.1k-blueviolet?logo=slack&amp;logoColor=white&style=flat-square\"></a>\n</p>\n\n<!-- start elevator-pitch -->\n\n\n## What is AnnLite?\n\n`AnnLite` is a *lightweight* and *embeddable* library for **fast** and **filterable** *approximate nearest neighbor search* (ANNS).\nIt allows to search for nearest neighbors in a dataset of millions of points with a Pythonic API.\n\n\n**Highlighted features:**\n\n- 🐥 **Easy-to-use**: a simple API is designed to be used with Python. It is easy to use and intuitive to set up to production.\n\n- 🐎 **Fast**: the library uses a highly optimized approximate nearest neighbor search algorithm (*HNSW*) to search for nearest neighbors.\n\n- 🔎 **Filterable**: the library allows you to search for nearest neighbors within a subset of the dataset.\n\n- 🍱 **Integration**: Smooth integration with neural search ecosystem including [Jina](https://github.com/jina-ai/jina) and [DocArray](https://github.com/jina-ai/docarray),\n    so that users can easily expose search API with **gRPC** and/or **HTTP**.\n\nThe library is easy to install and use. It is designed to be used with Python.\n\n<!---\nRead more on why should you use `AnnLite`: [here](), and compare to alternatives: [here]().\n-->\n\n## Installation\n\nTo use AnnLite, you need to first install it. The easiest way to install AnnLite is using `pip`:\n\n```bash\npip install -U annlite\n```\n\nor install from source:\n\n```bash\npython setup.py install\n```\n\n## Quick start\n\nBefore you start, you need to know some experience about [DocArray](https://github.com/jina-ai/docarray).\n`AnnLite` is designed to be used with [DocArray](https://github.com/jina-ai/docarray), so you need to know how to use `DocArray` first.\n\nFor example, you can create a `DocArray` with `1000` random vectors with `128` dimensions:\n\n```python\nfrom docarray import DocumentArray\nimport numpy as np\n\ndocs = DocumentArray.empty(1000)\ndocs.embeddings = np.random.random([1000, 128]).astype(np.float32)\n```\n\n\n### Index\n\nThen you can create an `AnnIndexer` to index the created `docs` and search for nearest neighbors:\n\n```python\nfrom annlite import AnnLite\n\nann = AnnLite(128, metric='cosine', data_path=\"/tmp/annlite_data\")\nann.index(docs)\n```\n\nNote that this will create a directory `/tmp/annlite_data` to persist the documents indexed.\nIf this directory already exists, the index will be loaded from the directory.\nAnd if you want to create a new index, you can delete the directory first.\n\n### Search\n\nThen you can search for nearest neighbors for some query docs with `ann.search()`:\n\n```python\nquery = DocumentArray.empty(5)\nquery.embeddings = np.random.random([5, 128]).astype(np.float32)\n\nresult = ann.search(query)\n```\n\nThen, you can inspect the retrieved docs for each query doc inside `query` matches:\n```python\nfor q in query:\n    print(f'Query {q.id}')\n    for k, m in enumerate(q.matches):\n        print(f'{k}: {m.id} {m.scores[\"cosine\"]}')\n```\n\n```bash\nQuery ddbae2073416527bad66ff186543eff8\n0: 47dcf7f3fdbe3f0b8d73b87d2a1b266f {'value': 0.17575037}\n1: 7f2cbb8a6c2a3ec7be024b750964f317 {'value': 0.17735684}\n2: 2e7eed87f45a87d3c65c306256566abb {'value': 0.17917466}\nQuery dda90782f6514ebe4be4705054f74452\n0: 6616eecba99bd10d9581d0d5092d59ce {'value': 0.14570713}\n1: d4e3147fc430de1a57c9883615c252c6 {'value': 0.15338594}\n2: 5c7b8b969d4381f405b8f07bc68f8148 {'value': 0.15743542}\n...\n```\n\nOr shorten the loop as one-liner using the element & attribute selector:\n\n```python\nprint(query['@m', ('id', 'scores__cosine')])\n```\n\n### Query\n\nYou can get specific document by its id:\n\n```python\ndoc = ann.get_doc_by_id('<doc_id>')\n```\n\nAnd you can also get the documents with `limit` and `offset`, which is useful for pagination:\n\n```python\ndocs = ann.get_docs(limit=10, offset=0)\n```\n\nFurthermore, you can also get the documents ordered by a specific column from the index:\n\n```python\ndocs = ann.get_docs(limit=10, offset=0, order_by='x', ascending=True)\n```\n\n**Note**: the `order_by` column must be one of the `columns` in the index.\n\n### Update\n\nAfter you have indexed the `docs`, you can update the docs in the index by calling `ann.update()`:\n\n```python\nupdated_docs = docs.sample(10)\nupdated_docs.embeddings = np.random.random([10, 128]).astype(np.float32)\n\nann.update(updated_docs)\n```\n\n\n### Delete\n\nAnd finally, you can delete the docs from the index by calling `ann.delete()`:\n\n```python\nto_delete = docs.sample(10)\nann.delete(to_delete)\n```\n\n## Search with filters\n\nTo support search with filters, the annlite must be created with `colums` parameter, which is a series of fields you want to filter by.\nAt the query time, the annlite will filter the dataset by providing `conditions` for certain fields.\n\n```python\nimport annlite\n\n# the column schema: (name:str, dtype:type, create_index: bool)\nann = annlite.AnnLite(128, columns=[('price', float)], data_path=\"/tmp/annlite_data\")\n```\n\nThen you can insert the docs, in which each doc has a field `price` with a float value contained in the `tags`:\n\n\n```python\nimport random\n\ndocs = DocumentArray.empty(1000)\ndocs = DocumentArray(\n    [\n        Document(id=f'{i}', tags={'price': random.random()})\n        for i in range(1000)\n    ]\n)\n\ndocs.embeddings = np.random.random([1000, 128]).astype(np.float32)\n\nann.index(docs)\n```\n\nThen you can search for nearest neighbors with filtering conditions as:\n\n```python\nquery = DocumentArray.empty(5)\nquery.embeddings = np.random.random([5, 128]).astype(np.float32)\n\nann.search(query, filter={\"price\": {\"$lte\": 50}}, limit=10)\nprint(f'the result with filtering:')\nfor i, q in enumerate(query):\n    print(f'query [{i}]:')\n    for m in q.matches:\n        print(f'\\t{m.id} {m.scores[\"euclidean\"].value} (price={m.tags[\"price\"]})')\n```\n\nThe `conditions` parameter is a dictionary of conditions. The key is the field name, and the value is a dictionary of conditions.\nThe query language is the same as  [MongoDB Query Language](https://docs.mongodb.com/manual/reference/operator/query/).\nWe currently support a subset of those selectors.\n\n- `$eq` - Equal to (number, string)\n- `$ne` - Not equal to (number, string)\n- `$gt` - Greater than (number)\n- `$gte` - Greater than or equal to (number)\n- `$lt` - Less than (number)\n- `$lte` - Less than or equal to (number)\n- `$in` - Included in an array\n- `$nin` - Not included in an array\n\n\nThe query will be performed on the field if the condition is satisfied. The following is an example of a query:\n\n1. A Nike shoes with white color\n\n    ```python\n    {\n      \"brand\": {\"$eq\": \"Nike\"},\n      \"category\": {\"$eq\": \"Shoes\"},\n      \"color\": {\"$eq\": \"White\"}\n    }\n    ```\n\n    We also support boolean operators `$or` and `$and`:\n\n    ```python\n    {\n      \"$and\":\n        {\n          \"brand\": {\"$eq\": \"Nike\"},\n          \"category\": {\"$eq\": \"Shoes\"},\n          \"color\": {\"$eq\": \"White\"}\n        }\n    }\n    ```\n\n2. A Nike shoes or price less than `100$`:\n\n    ```python\n    {\n        \"$or\":\n        {\n        \"brand\": {\"$eq\": \"Nike\"},\n        \"price\": {\"$lte\": 100}\n        }\n    }\n    ```\n\n## Dump and Load\n\nBy default, the hnsw index is in memory. You can dump the index to `data_path` by calling `.dump()`:\n\n```python\n\nfrom annlite import AnnLite\n\nann = AnnLite(128, metric='cosine', data_path=\"/path/to/data_path\")\nann.index(docs)\nann.dump()\n```\n\nAnd you can restore the hnsw index from `data_path` if it exists:\n\n```python\nnew_ann = AnnLite(128, metric='cosine', data_path=\"/path/to/data_path\")\n```\n\nIf you didn't dump the hnsw index, the index will be rebuilt from scratch. This will take a while.\n\n## Supported distance metrics\n\nThe annlite supports the following distance metrics:\n\n#### Supported distances:\n\n| Distance                                                             |       parameter |                                                Equation |\n|----------------------------------------------------------------------|----------------:|--------------------------------------------------------:|\n| [Euclidean](https://en.wikipedia.org/wiki/Euclidean_distance)        |     `euclidean` |                                d = sqrt(sum((Ai-Bi)^2)) |\n| [Inner product](https://en.wikipedia.org/wiki/Inner_product_space)   | `inner_product` |                                   d = 1.0 - sum(Ai\\*Bi) |\n| [Cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity) |        `cosine` | d = 1.0 - sum(Ai\\*Bi) / sqrt(sum(Ai\\*Ai) * sum(Bi\\*Bi)) |\n\nNote that inner product is not an actual metric. An element can be closer to some other element than to itself.\nThat allows some speedup if you remove all elements that are not the closest to themselves from the index, e.g.,\n`inner_product([1.0, 1.0], [1.0. 1.0]) < inner_product([1.0, 1.0], [2.0, 2.0])`\n\n\n## HNSW algorithm parameters\n\nThe HNSW algorithm has several parameters that can be tuned to improve the search performance.\n\n### Search parameters\n\n- `ef_search` - The size of the dynamic list for the nearest neighbors during search (default: `50`).\nThe larger the value, the more accurate the search results, but the slower the search speed.\nThe `ef_search` must be larger than `limit` parameter in `search(..., limit)`.\n\n- `limit` - The maximum number of results to return (default: `10`).\n\n## Construction parameters\n\n- `max_connection` - The number of bi-directional links created for every new element during construction (default: `16`).\nReasonable range is from `2` to `100`. Higher values works better for dataset with higher dimensionality and/or high recall.\nThis parameter also affects the memory consumption during construction, which is roughly `max_connection * 8-10` bytes per stored element.\n\n    As an example for `n_dim=4` random vectors optimal `max_connection` for search is somewhere around `6`,\nwhile for high dimensional datasets, higher `max_connection` are required (e.g. `M=48-64`) for optimal performance at high recall.\nThe range `max_connection=12-48` is ok for the most of the use cases.\nWhen `max_connection` is changed one has to update the other parameters.\nNonetheless, `ef_search` and `ef_construction` parameters can be roughly estimated by assuming that `max_connection * ef_{construction}` is a constant.\n\n\n- `ef_construction`: The size of the dynamic list for the nearest neighbors during construction (default: `200`).\nHigher values give better accuracy, but increase construction time and memory consumption.\nAt some point, increasing `ef_construction` does not give any more accuracy.\nTo set `ef_construction` to a reasonable value, one can measure the recall: if the recall is lower than 0.9, then increase `ef_construction` and re-run the search.\n\nTo set the parameters, you can define them when creating the annlite:\n\n```python\nfrom annlite import AnnLite\n\nann = AnnLite(128, columns=[('price', float)], data_path=\"/tmp/annlite_data\", ef_construction=200, max_connection=16)\n```\n\n## Benchmark\n\nOne can run `executor/benchmark.py` to get a quick performance overview.\n\n|Stored data| Indexing time | Query size=1 | Query size=8 | Query size=64|\n|---|---|---|---|---|\n|10000 | 2.970 | 0.002 | 0.013 | 0.100|\n|100000 | 76.474 | 0.011 | 0.078 | 0.649|\n|500000 | 467.936 | 0.046 | 0.356 | 2.823|\n|1000000 | 1025.506 | 0.091 | 0.695 | 5.778|\n\nResults with filtering can be generated from `examples/benchmark_with_filtering.py`. This script should produce a table similar to:\n\n| Stored data |% same filter| Indexing time | Query size=1 | Query size=8 | Query size=64|\n|-----|-----|-----|-----|-----|-----|\n| 10000 | 5  | 2.869 | 0.004 | 0.030 | 0.270 |\n| 10000 | 15 | 2.869 | 0.004 | 0.035 | 0.294 |\n| 10000 | 20 | 3.506 | 0.005 | 0.038 | 0.287 |\n| 10000 | 30 | 3.506 | 0.005 | 0.044 | 0.356 |\n| 10000 | 50 | 3.506 | 0.008 | 0.064 | 0.484 |\n| 10000 | 80 | 2.869 | 0.013 | 0.098 | 0.910 |\n| 100000 | 5 | 75.960 | 0.018 | 0.134 | 1.092 |\n| 100000 | 15 | 75.960 | 0.026 | 0.211 | 1.736 |\n| 100000 | 20 | 78.475 | 0.034 | 0.265 | 2.097 |\n| 100000 | 30 | 78.475 | 0.044 | 0.357 | 2.887 |\n| 100000 | 50 | 78.475 | 0.068 | 0.565 | 4.383 |\n| 100000 | 80 | 75.960 | 0.111 | 0.878 | 6.815 |\n| 500000 | 5 | 497.744 | 0.069 | 0.561 | 4.439 |\n| 500000 | 15 | 497.744 | 0.134 | 1.064 | 8.469 |\n| 500000 | 20 | 440.108 | 0.152 | 1.199 | 9.472 |\n| 500000 | 30 | 440.108 | 0.212 | 1.650 | 13.267 |\n| 500000 | 50 | 440.108 | 0.328 | 2.637 | 21.961 |\n| 500000 | 80 | 497.744 | 0.580 | 4.602 | 36.986 |\n| 1000000 | 5 | 1052.388 | 0.131 | 1.031 | 8.212 |\n| 1000000 | 15 | 1052.388 | 0.263 | 2.191 | 16.643 |\n| 1000000 | 20 | 980.598 | 0.351 | 2.659 | 21.193 |\n| 1000000 | 30 | 980.598 | 0.461 | 3.713 | 29.794 |\n| 1000000 | 50 | 980.598 | 0.732 | 5.975 | 47.356 |\n| 1000000 | 80 | 1052.388 | 1.151 | 9.255 | 73.552 |\n\n\nNote that:\n- query times presented are represented in seconds.\n- `% same filter`  indicates the amount of data that verifies a filter in the database.\n    - For example, if `% same filter = 10` and `Stored data = 1_000_000` then it means `100_000` example verify the filter.\n\n\n## Next steps\n\nIf you already have experience with Jina and DocArray, you can start using `AnnLite` right away.\n\nOtherwise, you can check out this advanced tutorial to learn how to use `AnnLite`: [here]() in practice.\n\n\n## 🙋 FAQ\n\n**1. Why should I use `AnnLite`?**\n\n`AnnLite` is easy to use and intuitive to set up in production. It is also very fast and memory efficient, making it a great choice for approximate nearest neighbor search.\n\n**2. How do I use `AnnLite` with Jina?**\n\nWe have implemented an executor for `AnnLite` that can be used with Jina.\n\n```python\nfrom jina import Flow\n\nwith Flow().add(uses='jinahub://AnnLiteIndexer', uses_with={'n_dim': 128}) as f:\n    f.post('/index', inputs=docs)\n```\n\n3. Does `AnnLite` support search with filters?\n\n```text\nYes.\n```\n\n\n## Documentation\n\nYou can find the documentation on [Github]() and [ReadTheDocs]()\n\n## 🤝 Contribute and spread the word\n\nWe are also looking for contributors who want to help us improve: code, documentation, issues, feedback! Here is how you can get started:\n\n- Have a look through GitHub issues labeled \"Good first issue\".\n- Read our Contributor Covenant Code of Conduct\n- Open an issue or submit your pull request!\n\n\n## License\n\n`AnnLite` is licensed under the [Apache License 2.0]().\n"
  },
  {
    "path": "annlite/__init__.py",
    "content": "__version__ = '0.5.11'\n\nfrom .index import AnnLite\n"
  },
  {
    "path": "annlite/container.py",
    "content": "import warnings\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Dict, List, Optional, Tuple\n\nimport numpy as np\nfrom docarray import Document, DocumentArray\nfrom loguru import logger\n\nif TYPE_CHECKING:\n    from .core.codec.pq import PQCodec\n    from .core.codec.projector import ProjectorCodec\n\nfrom .core.index.hnsw import HnswIndex\nfrom .enums import Metric\nfrom .storage.base import ExpandMode\nfrom .storage.kv import DocStorage\nfrom .storage.table import CellTable, MetaTable\n\nVALID_FILTERABLE_DATA_TYPES = [int, str, float]\n\n\nclass CellContainer:\n    def __init__(\n        self,\n        n_dim: int,\n        metric: Metric = Metric.COSINE,\n        n_cells: int = 1,\n        projector_codec: Optional['ProjectorCodec'] = None,\n        pq_codec: Optional['PQCodec'] = None,\n        initial_size: Optional[int] = None,\n        expand_step_size: int = 50000,\n        expand_mode: 'ExpandMode' = ExpandMode.STEP,\n        filterable_attrs: Optional[Dict] = None,\n        serialize_config: Optional[Dict] = None,\n        data_path: 'Path' = Path('./data'),\n        **kwargs,\n    ):\n        self.n_dim = n_dim\n        self.metric = metric\n        self.n_cells = n_cells\n        self.n_components = projector_codec.n_components if projector_codec else None\n        self.data_path = data_path\n        self.serialize_config = serialize_config\n\n        self._pq_codec = pq_codec\n        self._projector_codec = projector_codec\n\n        self._vec_indexes = [\n            HnswIndex(\n                dim=self.n_components or n_dim,\n                metric=metric,\n                initial_size=initial_size,\n                expand_step_size=expand_step_size,\n                expand_mode=expand_mode,\n                pq_codec=pq_codec,\n                **kwargs,\n            )\n            for _ in range(n_cells)\n        ]\n\n        self._doc_stores = [\n            DocStorage(\n                data_path / f'cell_{_}',\n                serialize_config=serialize_config or {},\n                lock=True,\n            )\n            for _ in range(n_cells)\n        ]\n\n        columns = []\n        if filterable_attrs:\n            for attr_name, attr_type in filterable_attrs.items():\n                if isinstance(attr_type, str):\n                    attr_type = eval(attr_type)\n\n                if attr_type not in VALID_FILTERABLE_DATA_TYPES:\n                    raise ValueError(\n                        f'Invalid filterable attribute type `{attr_type}` for attribute `{attr_name}`. '\n                    )\n                columns.append((attr_name, attr_type))\n\n        self._cell_tables = [\n            CellTable(f'table_{c}', columns=columns) for c in range(n_cells)\n        ]\n\n        self._meta_table = MetaTable('metas', data_path=data_path, in_memory=True)\n\n    def ivf_search(\n        self,\n        x: 'np.ndarray',\n        cells: 'np.ndarray',\n        where_clause: str = '',\n        where_params: Tuple = (),\n        limit: int = 10,\n    ):\n        dists = []\n\n        doc_idx = []\n        cell_ids = []\n        count = 0\n        for cell_id in cells:\n            cell_table = self.cell_table(cell_id)\n            cell_size = cell_table.count()\n            if cell_size == 0:\n                continue\n\n            indices = None\n            if where_clause:\n                indices = cell_table.query(\n                    where_clause=where_clause, where_params=where_params\n                )\n\n                if len(indices) == 0:\n                    continue\n\n                indices = np.array(indices, dtype=np.int64)\n\n            _dists, _doc_idx = self.vec_index(cell_id).search(\n                x, limit=min(limit, cell_size), indices=indices\n            )\n\n            if count >= limit and _dists[0] > dists[-1][-1]:\n                continue\n\n            dists.append(_dists)\n            doc_idx.append(_doc_idx)\n            cell_ids.extend([cell_id] * len(_dists))\n            count += len(_dists)\n\n        cell_ids = np.array(cell_ids, dtype=np.int64)\n        if len(dists) != 0:\n            dists = np.hstack(dists)\n            doc_idx = np.hstack(doc_idx)\n\n            indices = dists.argsort(axis=0)[:limit]\n            dists = dists[indices]\n            cell_ids = cell_ids[indices]\n            doc_idx = doc_idx[indices]\n\n        doc_ids = []\n        for cell_id, offset in zip(cell_ids, doc_idx):\n            doc_id = self.cell_table(cell_id).get_docid_by_offset(offset)\n            doc_ids.append(doc_id)\n        return dists, doc_ids, cell_ids\n\n    def filter_cells(\n        self,\n        cells: 'np.ndarray',\n        where_clause: str = '',\n        where_params: Tuple = (),\n        limit: int = -1,\n        offset: int = 0,\n        order_by: Optional[str] = None,\n        ascending: bool = True,\n        include_metadata: bool = False,\n    ):\n        result = DocumentArray()\n        if len(cells) > 1 and offset > 0:\n            raise ValueError('Offset is not supported for multiple cells')\n\n        for cell_id in cells:\n            cell_table = self.cell_table(cell_id)\n            cell_size = cell_table.count()\n            if cell_size == 0:\n                continue\n\n            indices = cell_table.query(\n                where_clause=where_clause,\n                where_params=where_params,\n                order_by=order_by,\n                limit=limit,\n                offset=offset,\n                ascending=ascending,\n            )\n\n            if len(indices) == 0:\n                continue\n\n            for offset in indices:\n                doc_id = self.cell_table(cell_id).get_docid_by_offset(offset)\n                doc = Document(id=doc_id)\n                if include_metadata or (len(cells) > 1 and order_by):\n                    doc = self.doc_store(cell_id).get([doc_id])[0]\n\n                result.append(doc)\n\n            if not order_by and len(result) >= limit > 0:\n                break\n\n        # reordering the results from multiple cells\n        if order_by and len(cells) > 1:\n            result = sorted(\n                result, key=lambda d: d.tags.get(order_by), reverse=not ascending\n            )\n            if limit > 0:\n                result = result[:limit]\n            result = DocumentArray(result)\n\n        return result\n\n    def search_cells(\n        self,\n        query: 'np.ndarray',\n        cells: 'np.ndarray',\n        where_clause: str = '',\n        where_params: Tuple = (),\n        limit: int = 10,\n        include_metadata: bool = False,\n    ):\n        if self._projector_codec:\n            query = self._projector_codec.encode(query)\n\n        topk_dists, topk_docs = [], []\n        for x, cell_idx in zip(query, cells):\n            # x.shape = (self.n_dim,)\n            dists, doc_ids, cells = self.ivf_search(\n                x,\n                cells=cell_idx,\n                where_clause=where_clause,\n                where_params=where_params,\n                limit=limit,\n            )\n\n            topk_dists.append(dists)\n            match_docs = DocumentArray()\n            for dist, doc_id, cell_id in zip(dists, doc_ids, cells):\n                doc = Document(id=doc_id)\n                if include_metadata:\n                    doc = self.doc_store(cell_id).get([doc_id])[0]\n\n                doc.scores[self.metric.name.lower()].value = dist\n                match_docs.append(doc)\n            topk_docs.append(match_docs)\n\n        return topk_dists, topk_docs\n\n    def _search_cells(\n        self,\n        query: 'np.ndarray',\n        cells: 'np.ndarray',\n        where_clause: str = '',\n        where_params: Tuple = (),\n        limit: int = 10,\n    ):\n        if self._projector_codec:\n            query = self._projector_codec.encode(query)\n\n        topk_dists, topk_ids = [], []\n        for x, cell_idx in zip(query, cells):\n            dists, ids, cells = self.ivf_search(\n                x,\n                cells=cell_idx,\n                where_clause=where_clause,\n                where_params=where_params,\n                limit=limit,\n            )\n            topk_dists.append(dists)\n            topk_ids.append(ids)\n\n        return topk_dists, [np.array(ids, dtype=int) for ids in topk_ids]\n\n    def insert(\n        self,\n        data: 'np.ndarray',\n        cells: 'np.ndarray',\n        docs: 'DocumentArray',\n        only_index: bool = False,\n    ):\n        assert len(docs) == len(data)\n\n        if self._projector_codec:\n            data = self._projector_codec.encode(data)\n\n        unique_cells, unique_cell_counts = np.unique(cells, return_counts=True)\n\n        if len(unique_cells) == 1:\n            cell_id = unique_cells[0]\n\n            offsets = self.cell_table(cell_id).insert(docs)\n            offsets = np.array(offsets, dtype=np.int64)\n\n            self.vec_index(cell_id).add_with_ids(data, offsets)\n            self._meta_table.bulk_add_address([d.id for d in docs], cells, offsets)\n\n            if not only_index:\n                self.doc_store(cell_id).insert(docs)\n\n        else:\n            for cell_id, cell_count in zip(unique_cells, unique_cell_counts):\n                # TODO: Jina should allow boolean filtering in docarray to avoid this\n                # and simply use cells == cell_index\n                indices = np.where(cells == cell_id)[0]\n                cell_docs = docs[indices.tolist()]\n\n                cell_offsets = self.cell_table(cell_id).insert(cell_docs)\n                cell_offsets = np.array(cell_offsets, dtype=np.int64)\n\n                cell_data = data[indices, :]\n\n                self.vec_index(cell_id).add_with_ids(cell_data, cell_offsets)\n                self._meta_table.bulk_add_address(\n                    [d.id for d in cell_docs], [cell_id] * cell_count, cell_offsets\n                )\n\n                if not only_index:\n                    self.doc_store(cell_id).insert(cell_docs)\n\n        logger.debug(f'{len(docs)} new docs added')\n\n    def _add_vecs(self, data: 'np.ndarray', cells: 'np.ndarray', offsets: 'np.ndarray'):\n        assert data.shape[0] == cells.shape[0]\n        assert data.shape[1] == self.n_dim\n\n        unique_cells, _ = np.unique(cells, return_counts=True)\n\n        for cell_id in unique_cells:\n            indices = cells == cell_id\n            x = data[indices, :]\n            ids = offsets[indices]\n\n            self.vec_index(cell_id).add_with_ids(x, ids)\n\n    def update(\n        self,\n        data: 'np.ndarray',\n        cells: 'np.ndarray',\n        docs: 'DocumentArray',\n        insert_if_not_found: bool = True,\n        raise_errors_on_not_found: bool = False,\n    ):\n        update_success = 0\n\n        new_data = []\n        new_cells = []\n        new_docs = []\n\n        for (\n            x,\n            doc,\n            cell_id,\n        ) in zip(data, docs, cells):\n            _cell_id, _offset = self._meta_table.get_address(doc.id)\n            if cell_id == _cell_id:\n                self.vec_index(cell_id).add_with_ids(x.reshape(1, -1), [_offset])\n                self.doc_store(cell_id).update([doc])\n                self.meta_table.add_address(doc.id, cell_id, _offset)\n                update_success += 1\n            elif _cell_id is None:\n                if raise_errors_on_not_found and not insert_if_not_found:\n                    raise Exception(\n                        f'The document (id={doc.id}) cannot be updated as'\n                        f'it is not found in the index'\n                    )\n                elif not (raise_errors_on_not_found or insert_if_not_found):\n                    warnings.warn(\n                        f'The document (id={doc.id}) cannot be updated as '\n                        f'it is not found in the index',\n                        RuntimeWarning,\n                    )\n                elif insert_if_not_found:\n                    new_data.append(x)\n                    new_cells.append(cell_id)\n                    new_docs.append(doc)\n                    update_success += 1\n                else:\n                    continue\n            else:\n                # DELETE and INSERT\n                self.vec_index(_cell_id).delete(_offset)\n                self.cell_table(_cell_id).delete_by_offset(_offset)\n                self.doc_store(_cell_id).delete([doc.id])\n\n                new_data.append(x)\n                new_cells.append(cell_id)\n                new_docs.append(doc)\n                update_success += 1\n\n        if len(new_data) > 0:\n            new_data = np.stack(new_data)\n            new_cells = np.array(new_cells, dtype=np.int64)\n\n            self.insert(new_data, new_cells, new_docs)\n\n        logger.debug(\n            f'total items for updating: {len(docs)}, ' f'success: {update_success}'\n        )\n\n    def delete(\n        self,\n        ids: List[str],\n        raise_errors_on_not_found: bool = False,\n    ):\n        delete_success = 0\n\n        for doc_id in ids:\n            cell_id, offset = self._meta_table.get_address(doc_id)\n            if cell_id is not None:\n                self.vec_index(cell_id).delete([offset])\n                self.cell_table(cell_id).delete_by_offset(offset)\n                self.doc_store(cell_id).delete([doc_id])\n                self.meta_table.delete_address(doc_id)\n                delete_success += 1\n            else:\n                if raise_errors_on_not_found:\n                    raise Exception(\n                        f'The document (id={doc_id}) cannot be updated as'\n                        f'it is not found in the index'\n                    )\n                else:\n                    continue\n\n        logger.debug(\n            f'total items for updating: {len(ids)}, ' f'success: {delete_success}'\n        )\n\n    def _rebuild_database(self):\n        \"\"\"rebuild doc_store and meta_table after annlite download databse from hubble\"\"\"\n\n        self._doc_stores = [\n            DocStorage(\n                self.data_path / f'cell_{_}',\n                serialize_config=self.serialize_config or {},\n                lock=True,\n            )\n            for _ in range(self.n_cells)\n        ]\n        # self._meta_table = MetaTable('metas', data_path=self.data_path, in_memory=False)\n\n    def _get_doc_by_id(self, doc_id: str):\n        cell_id = 0\n        if self.n_cells > 1:\n            cell_id, _ = self._meta_table.get_address(doc_id)\n\n        da = self.doc_store(cell_id).get([doc_id])\n        return da[0] if len(da) > 0 else None\n\n    def documents_generator(self, cell_id: int, batch_size: int = 1000):\n        for docs in self.doc_store(cell_id).batched_iterator(batch_size=batch_size):\n            yield docs\n\n    @property\n    def cell_tables(self):\n        return self._cell_tables\n\n    @property\n    def cell_indexes(self):\n        return self._vec_indexes\n\n    def cell_table(self, cell_id: int):\n        return self._cell_tables[cell_id]\n\n    def doc_store(self, cell_id: int):\n        return self._doc_stores[cell_id]\n\n    def vec_index(self, cell_id: int):\n        return self._vec_indexes[cell_id]\n\n    @property\n    def meta_table(self):\n        return self._meta_table\n\n    @property\n    def total_docs(self):\n        return sum([store.size for store in self._doc_stores])\n\n    @property\n    def index_size(self):\n        return sum([table.size for table in self._cell_tables])\n"
  },
  {
    "path": "annlite/core/__init__.py",
    "content": "from .codec import PQCodec, ProjectorCodec, VQCodec\n"
  },
  {
    "path": "annlite/core/codec/__init__.py",
    "content": "from .pq import PQCodec\nfrom .projector import ProjectorCodec\nfrom .vq import VQCodec\n"
  },
  {
    "path": "annlite/core/codec/base.py",
    "content": "import pickle\nfrom abc import ABC, abstractmethod\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n    from pathlib import Path\n\n\nclass BaseCodec(ABC):\n    def __init__(self, require_train: bool = True):\n        self.require_train = require_train\n        self._is_trained = False if require_train else True\n\n    @abstractmethod\n    def fit(self, *args, **kwargs):\n        pass\n\n    @abstractmethod\n    def encode(self):\n        pass\n\n    @abstractmethod\n    def decode(self):\n        pass\n\n    def dump(self, target_path: 'Path'):\n        pickle.dump(self, target_path.open('wb'), protocol=4)\n\n    @staticmethod\n    def load(from_path: 'Path'):\n        return pickle.load(from_path.open('rb'))\n\n    @property\n    def is_trained(self):\n        return self._is_trained\n\n    def _check_trained(self):\n        assert self.is_trained is True, f'{self.__class__.__name__} requires training'\n"
  },
  {
    "path": "annlite/core/codec/pq.py",
    "content": "from argparse import ArgumentError\n\nimport numpy as np\nfrom scipy.cluster.vq import vq\n\nfrom annlite import pq_bind\n\nfrom ...enums import Metric\nfrom ...math import l2_normalize\nfrom ...profile import time_profile\nfrom .base import BaseCodec\n\n# from pqlite.pq_bind import precompute_adc_table, dist_pqcodes_to_codebooks\n\n\nclass PQCodec(BaseCodec):\n    \"\"\"Implementation of Product Quantization (PQ) [Jegou11]_.\n\n    For the indexing phase of database vectors,\n    a `D`-dim input vector is divided into `M` `D`/`M`-dim sub-vectors.\n    Each sub-vector is quantized into a small integer via `Ks` codewords.\n\n    For the querying phase, given a new `D`-dim query vector, the distance between the query\n    and the database PQ-codes are efficiently approximated via Asymmetric Distance.\n    All vectors must be np.ndarray with np.float32\n\n    .. [Jegou11] H. Jegou et al., \"Product Quantization for Nearest Neighbor Search\", IEEE TPAMI 2011\n\n    :param d_vector: the dimensionality of input vectors\n    :param n_subvectors: The number of sub-space\n    :param n_clusters: The number of codewords for each subspace\n            (typically 256, so that each sub-vector is quantized\n            into 256 bits pqlite.utils.asymmetric_distance= 1 byte = uint8)\n    :param n_init: Number of times K-Means is trained with different centroid seeds. Best result of\n                   the `n_init` consecutive runs is selected.\n    \"\"\"\n\n    def __init__(\n        self,\n        dim: int,\n        n_subvectors: int = 8,\n        n_clusters: int = 256,\n        metric: Metric = Metric.EUCLIDEAN,\n        n_init: int = 4,\n    ):\n        super(PQCodec, self).__init__(require_train=True)\n        self.dim = dim\n        self.n_subvectors = n_subvectors\n        self.n_clusters = n_clusters\n\n        assert (\n            dim % n_subvectors == 0\n        ), 'input dimension must be dividable by number of sub-space'\n        self.d_subvector = dim // n_subvectors\n\n        self.code_dtype = (\n            np.uint8\n            if n_clusters <= 2**8\n            else (np.uint16 if n_clusters <= 2**16 else np.uint32)\n        )\n\n        # assert (\n        #    metric == Metric.EUCLIDEAN\n        # ), f'The distance metric `{metric.name}` is not supported yet!'\n        self.metric = metric\n\n        self.normalize_input = False\n        if self.metric == Metric.COSINE:\n            self.normalize_input = True\n\n        self._codebooks = np.zeros(\n            (self.n_subvectors, self.n_clusters, self.d_subvector), dtype=np.float32\n        )\n        self.kmeans = []\n        self.n_init = n_init\n\n    def __hash__(self):\n        return hash(\n            (\n                self.__class__.__name__,\n                self.dim,\n                self.n_subvectors,\n                self.n_clusters,\n                self.metric,\n                self.code_dtype,\n            )\n        )\n\n    def fit(self, x: 'np.ndarray', iter: int = 100):\n        \"\"\"Train the K-Means for each cartesian product\n\n        :param x: Training vectors with shape=(N, D)\n        :param iter: Number of iterations in Kmeans\n        \"\"\"\n        from sklearn.cluster import KMeans\n\n        assert x.dtype == np.float32\n        assert x.ndim == 2\n\n        if self.normalize_input:\n            x = l2_normalize(x)\n\n        # [m][ks][ds]: m-th subspace, ks-the codeword, ds-th dim\n        self._codebooks = np.zeros(\n            (self.n_subvectors, self.n_clusters, self.d_subvector), dtype=np.float32\n        )\n        for m in range(self.n_subvectors):\n            kmeans = KMeans(\n                n_clusters=self.n_clusters, max_iter=iter, n_init=self.n_init\n            )\n            self.kmeans.append(kmeans)\n            self.kmeans[m].fit(x[:, m * self.d_subvector : (m + 1) * self.d_subvector])\n            self._codebooks[m] = self.kmeans[m].cluster_centers_\n\n        self._is_trained = True\n\n    def partial_fit(self, x: 'np.ndarray'):\n        \"\"\"Given a batch of training vectors, update the internal MiniBatchKMeans.\n        This method is specially designed to be used when data does not fit in memory.\n\n        :param x: Training vectors with shape=(N, D)\n        \"\"\"\n        assert x.ndim == 2\n\n        if self.normalize_input:\n            x = l2_normalize(x)\n\n        if len(self.kmeans) > 0:\n            for m in range(self.n_subvectors):\n                self.kmeans[m].partial_fit(\n                    x[:, m * self.d_subvector : (m + 1) * self.d_subvector]\n                )\n        else:\n            from sklearn.cluster import MiniBatchKMeans\n\n            for m in range(self.n_subvectors):\n                self.kmeans.append(MiniBatchKMeans(n_clusters=self.n_clusters))\n\n            for m in range(self.n_subvectors):\n                self.kmeans[m].partial_fit(\n                    x[:, m * self.d_subvector : (m + 1) * self.d_subvector]\n                )\n\n    def build_codebook(self):\n        \"\"\"Constructs sub-codebooks from the current parameters of the models in `self.kmeans`\n        This step is not necessary if full KMeans is trained used calling `.fit`.\n        \"\"\"\n\n        self._codebooks = np.zeros(\n            (self.n_subvectors, self.n_clusters, self.d_subvector), dtype=np.float32\n        )\n\n        for m in range(self.n_subvectors):\n            self._codebooks[m] = self.kmeans[m].cluster_centers_\n\n        self._is_trained = True\n\n    def encode(self, x: 'np.ndarray'):\n        \"\"\"Encode input vectors into PQ-codes.\n\n        :param x: Input vectors with shape=(N, D) and dtype=np.float32.\n        :return: np.ndarray: PQ codes with shape=(N, M) and dtype=self.code_dtype\n        \"\"\"\n        assert x.dtype == np.float32\n        assert x.ndim == 2\n        N, D = x.shape\n        assert (\n            D == self.d_subvector * self.n_subvectors\n        ), 'input dimension must be Ds * M'\n\n        # codes[n][m] : code of n-th vec, m-th subspace\n        codes = np.empty((N, self.n_subvectors), dtype=self.code_dtype)\n        for m in range(self.n_subvectors):\n            sub_vecs = x[:, m * self.d_subvector : (m + 1) * self.d_subvector]\n            codes[:, m], _ = vq(sub_vecs, self.codebooks[m])\n\n        return codes\n\n    def decode(self, codes: 'np.ndarray'):\n        \"\"\"Given PQ-codes, reconstruct original D-dimensional vectors\n        approximately by fetching the codewords.\n\n        :param codes: PQ-cdoes with shape=(N, M) and dtype=self.code_dtype.\n            Each row is a PQ-code\n        :return: Reconstructed vectors with shape=(N, D) and dtype=np.float32\n        \"\"\"\n        assert codes.ndim == 2\n        N, M = codes.shape\n        assert M == self.n_subvectors\n        assert codes.dtype == self.code_dtype\n\n        vecs = np.empty((N, self.d_subvector * self.n_subvectors), dtype=np.float32)\n        for m in range(self.n_subvectors):\n            vecs[:, m * self.d_subvector : (m + 1) * self.d_subvector] = self.codebooks[\n                m\n            ][codes[:, m], :]\n\n        return vecs\n\n    def precompute_adc(self, query: object) -> object:\n        \"\"\"Compute a distance table for a query vector.\n        The distances are computed by comparing each sub-vector of the query\n        to the codewords for each sub-subspace.\n        `dtable[m][ks]` contains the squared Euclidean distance between\n        the `m`-th sub-vector of the query and the `ks`-th codeword\n        for the `m`-th sub-space (`self.codewords[m][ks]`).\n\n        :param query: Input vector with shape=(D, ) and dtype=np.float32\n        :return: Distance table. which contains dtable with shape=(M, Ks)\n            and dtype=np.float32\n        \"\"\"\n        assert query.dtype == np.float32\n        assert query.ndim == 1, 'input must be a single vector'\n\n        # dtable[m] : distance between m-th subvec and m-th codewords (m-th subspace)\n        # dtable[m][ks] : distance between m-th subvec and ks-th codeword of m-th codewords\n\n        # Warning: the following line produces `ValueError: buffer source array is read-only`\n        # if no `const` is used in the cython implementation using a memoryview\n        dtable = pq_bind.precompute_adc_table(\n            query, self.d_subvector, self.n_clusters, self.codebooks\n        )\n\n        return DistanceTable(dtable)\n\n    @property\n    def codebooks(self):\n        return self._codebooks\n\n    # trained pq interface ----------------\n    def get_codebook(self) -> 'np.ndarray':\n        \"\"\"Return the codebook parameters.\n\n        Expect a 3-dimensional matrix is returned,\n        with shape (`n_subvectors`, `n_clusters`, `d_subvector`) and dtype float32\n        \"\"\"\n        return np.ascontiguousarray(self.codebooks, dtype='float32')\n\n    def get_subspace_splitting(self):\n        \"\"\"Return subspace splitting setting\n\n        :return: tuple of (`n_subvectors`, `n_clusters`, `d_subvector`)\n        \"\"\"\n        return (self.n_subvectors, self.n_clusters, self.d_subvector)\n\n    # def get_dist_mat(self, x: np.ndarray):\n    #     \"\"\"Return the distance tables in form of matrix for multiple queries\n\n    #     :param query: shape('N', 'D'),\n\n    #     :return: ndarray with shape('N', `n_subvectors`, `n_clusters`)\n\n    #     .. note::\n    #         _description_\n    #     \"\"\"\n    #     assert x.dtype == np.float32\n    #     assert x.ndim == 2\n    #     N, D = x.shape\n    #     assert (\n    #         D == self.d_subvector * self.n_subvectors\n    #     ), 'input dimension must be Ds * M'\n    #     if self.normalize_input:\n    #         x = l2_normalize(x)\n\n    #     x = x.reshape(\n    #         N,\n    #         self.n_subvectors,\n    #         1,\n    #         self.d_subvector,\n    #     )\n    #     if self.metric == Metric.EUCLIDEAN:\n    #         # (1, n_subvectors, n_clusters, d_subvector)\n    #         codebook = self.codebooks[np.newaxis, ...]\n\n    #         # broadcast to (N, n_subvectors, n_clusters, d_subvector)\n    #         dist_vector = (x - codebook) ** 2\n\n    #         # reduce to (N, n_subvectors, n_clusters)\n    #         dist_mat = np.sum(dist_vector, axis=3)\n    #     elif self.metric in [Metric.INNER_PRODUCT, Metric.COSINE]:\n    #         # (1, n_subvectors, n_clusters, d_subvector)\n    #         codebook = self.codebooks[np.newaxis, ...]\n\n    #         # broadcast to (N, n_subvectors, n_clusters, d_subvector)\n    #         dist_vector = x * codebook\n\n    #         # reduce to (N, n_subvectors, n_clusters)\n    #         dist_mat = 1 / self.n_clusters - np.sum(dist_vector, axis=3)\n    #     else:\n    #         raise ArgumentError(f'Unable support metrics {self.metric}')\n    #     return np.ascontiguousarray(dist_mat, dtype='float32')\n\n    def get_dist_mat(self, x: np.ndarray):\n        \"\"\"Return the distance tables in form of matrix for multiple queries\n\n        :param query: shape('N', 'D'),\n\n        :return: ndarray with shape('N', `n_subvectors`, `n_clusters`)\n\n        .. note::\n            _description_\n        \"\"\"\n        assert x.dtype == np.float32\n        assert x.ndim == 2\n        N, D = x.shape\n        assert (\n            D == self.d_subvector * self.n_subvectors\n        ), 'input dimension must be Ds * M'\n        if self.normalize_input:\n            x = l2_normalize(x)\n\n        if self.metric == Metric.EUCLIDEAN:\n            dist_mat = pq_bind.batch_precompute_adc_table(\n                x, self.d_subvector, self.n_clusters, self.codebooks\n            )\n        elif self.metric in [Metric.INNER_PRODUCT, Metric.COSINE]:\n            dist_mat = 1 / self.n_clusters - np.array(\n                pq_bind.batch_precompute_adc_table_ip(\n                    x, self.d_subvector, self.n_clusters, self.codebooks\n                ),\n                dtype='float32',\n            )\n        else:\n            raise ArgumentError(f'Unable support metrics {self.metric}')\n        return np.ascontiguousarray(dist_mat, dtype='float32')\n\n    # -------------------------------------\n\n\nclass DistanceTable(object):\n    \"\"\"Distance table from query to codeworkds.\n    Given a query vector, a PQ/OPQ instance compute this DistanceTable class\n    using :func:`PQ.dtable` or :func:`OPQ.dtable`.\n    The Asymmetric Distance from query to each database codes can be computed\n    by :func:`DistanceTable.adist`.\n    Args:\n        dtable (np.ndarray): Distance table with shape=(M, Ks) and dtype=np.float32\n            computed by :func:`PQ.dtable` or :func:`OPQ.dtable`\n    Attributes:\n        dtable (np.ndarray): Distance table with shape=(M, Ks) and dtype=np.float32.\n            Note that dtable[m][ks] contains the squared Euclidean distance between\n            (1) m-th sub-vector of query and (2) ks-th codeword for m-th subspace.\n    \"\"\"\n\n    def __init__(self, dtable: 'np.ndarray'):\n\n        assert dtable.ndim == 2\n        self.dtable = dtable\n\n    def adist(self, codes):\n        \"\"\"Given PQ-codes, compute Asymmetric Distances between the query (self.dtable)\n        and the PQ-codes.\n        Args:\n            codes (np.ndarray): PQ codes with shape=(N, M) and\n                dtype=pq.code_dtype where pq is a pq instance that creates the codes\n        Returns:\n            np.ndarray: Asymmetric Distances with shape=(N, ) and dtype=np.float32\n        \"\"\"\n\n        assert codes.ndim == 2\n        dists = pq_bind.dist_pqcodes_to_codebooks(self.dtable, codes)\n\n        # The above line is equivalent to the followings:\n        # dists = np.zeros((N, )).astype(np.float32)\n        # for n in range(N):\n        #     for m in range(M):\n        #         dists[n] += self.dtable[m][codes[n][m]]\n        return dists\n"
  },
  {
    "path": "annlite/core/codec/projector.py",
    "content": "from typing import Optional\n\nimport numpy as np\n\nfrom .base import BaseCodec\n\n\nclass ProjectorCodec(BaseCodec):\n    \"\"\"Implementation of Projector.\n\n    :param n_components: number of components to keep.\n    :param whiten: when True (False by default) the components_ vectors are multiplied\n                    by the square root of n_samples and then divided by the singular\n                    values to ensure uncorrelated outputs with unit component-wise variances.\n    :param svd_solver:\n            If auto: The solver is selected by a default policy based on X.shape and\n            n_components: if the input data is larger than 500x500 and the number of\n            components to extract is lower than 80% of the smallest dimension of the\n            data, then the more efficient ‘randomized’ method is enabled. Otherwise\n            the exact full SVD is computed and optionally truncated afterwards.\n\n            If full: run exact full SVD calling the standard LAPACK solver via scipy.\n            linalg.svd and select the components by postprocessing.\n\n            If arpack: run SVD truncated to n_components calling ARPACK solver via\n            scipy.sparse.linalg.svds. It requires strictly 0 < n_components < min(X.shape).\n    \"\"\"\n\n    def __init__(\n        self,\n        dim: int,\n        n_components: int = 128,\n        whiten: Optional[bool] = False,\n        svd_solver: Optional[str] = 'auto',\n    ):\n        super(ProjectorCodec, self).__init__(require_train=True)\n        self.dim = dim\n        self.n_components = n_components\n        assert self.dim >= self.n_components, (\n            f'the dimension after projector should be less than original dimension, got '\n            f'original dimension: {self.dim} and projector dimension: {self.n_components}'\n        )\n\n        self.whiten = whiten\n        self.svd_solver = svd_solver\n\n        self.pca = None\n\n    def __hash__(self):\n        return hash(\n            (\n                self.__class__.__name__,\n                self.dim,\n                self.n_components,\n                self.whiten,\n                self.svd_solver,\n            )\n        )\n\n    def fit(self, x: 'np.ndarray'):\n        \"\"\"Train projector model\n\n        :param x: Training vectors with shape=(N, D)\n        \"\"\"\n        assert x.ndim == 2\n        assert (\n            x.shape[1] == self.dim,\n        ), 'dimension of input data must be equal to \"dim\"'\n        assert (\n            x.shape[0] > self.n_components\n        ), 'number of input data must be larger than or equal to n_components'\n\n        if self.pca is None:\n            from sklearn.decomposition import PCA\n\n            self.pca = PCA(\n                n_components=self.n_components,\n                whiten=self.whiten,\n                svd_solver=self.svd_solver,\n            )\n\n        self.pca.fit(x)\n        self._is_trained = True\n\n    def partial_fit(self, x: 'np.ndarray'):\n        \"\"\"Given a batch of training vectors, update the internal projector.\n        This method is specially designed to be used when data does not fit in memory.\n\n        :param x: Training vectors with shape=(N, D)\n        \"\"\"\n\n        assert x.ndim == 2\n        assert x.shape[1] == self.dim, 'dimension of input data must be equal to \"dim\"'\n        assert (\n            x.shape[0] > self.n_components\n        ), 'number of input data must be larger than or equal to n_components'\n\n        if self.pca is None:\n            from sklearn.decomposition import IncrementalPCA\n\n            self.pca = IncrementalPCA(\n                n_components=self.n_components,\n                whiten=self.whiten,\n            )\n\n        self.pca.partial_fit(x)\n        self._is_trained = True\n\n    def encode(self, x: 'np.ndarray'):\n        \"\"\"Encode input vectors using projector.\n\n        :param x: Input vectors with shape=(N, D)\n        :return: np.ndarray: transformed vectors using projector.\n        \"\"\"\n        assert x.ndim == 2\n        assert x.shape[1] == self.dim, 'dimension of input data must be equal to \"dim\"'\n\n        return self.pca.transform(x)\n\n    def decode(self, x: 'np.ndarray'):\n        \"\"\"Given transformed vectors, reconstruct original D-dimensional vectors\n        approximately.\n\n        :param x: vectors with shape=(N, self.n_components).\n        :return: Reconstructed vectors with shape=(N, D)\n        \"\"\"\n        assert x.ndim == 2\n        assert x.shape[1] == self.n_components\n\n        return self.pca.inverse_transform(x)\n\n    @property\n    def components(self):\n        \"\"\"Principal axes in feature space, representing the directions of maximum\n        variance in the data.\n        \"\"\"\n        self._check_trained()\n        return self.pca.components_\n\n    @property\n    def explained_variance_ratio(self):\n        \"\"\"Percentage of variance explained by each of the selected components.\"\"\"\n        self._check_trained()\n        return self.pca.explained_variance_ratio_\n\n    @property\n    def mean(self):\n        \"\"\"Per-feature empirical mean.\"\"\"\n        self._check_trained()\n        return self.pca.mean_\n\n    @property\n    def var(self):\n        \"\"\"Per-feature empirical variance\"\"\"\n        self._check_trained()\n        return self.pca.var_\n"
  },
  {
    "path": "annlite/core/codec/vq.py",
    "content": "import numpy as np\nfrom scipy.cluster.vq import vq\n\nfrom ...enums import Metric\nfrom .base import BaseCodec\n\n\nclass VQCodec(BaseCodec):\n    def __init__(\n        self,\n        n_clusters: int,\n        metric: Metric = Metric.EUCLIDEAN,\n        iter: int = 100,\n        n_init: int = 4,\n        *args,\n        **kwargs\n    ):\n        super(VQCodec, self).__init__(require_train=True)\n        self.n_clusters = n_clusters\n\n        # assert (\n        #    metric == Metric.EUCLIDEAN\n        # ), f'The distance metric `{metric.name}` is not supported yet!'\n        self.metric = metric\n        self._codebook = None\n        self.iter = iter\n        self.kmeans = None\n        self.n_init = n_init\n\n    def __hash__(self):\n        return hash((self.__class__.__name__, self.n_clusters, self.metric))\n\n    def fit(self, x: 'np.ndarray'):\n        \"\"\"Given training vectors, run k-means for each sub-space and create\n           codewords for each sub-space.\n\n        :param x: Training vectors with shape=(N, D) and dtype=np.float32.\n        :param iter: The number of iteration for k-means\n        \"\"\"\n\n        from sklearn.cluster import KMeans\n\n        assert x.dtype == np.float32\n        assert x.ndim == 2\n\n        self.kmeans = KMeans(self.n_clusters, max_iter=self.iter, n_init=self.n_init)\n        self.kmeans.fit(x)\n        self._codebook = self.kmeans.cluster_centers_\n        self._is_trained = True\n\n    def partial_fit(self, x: 'np.ndarray'):\n        \"\"\"Given a batch of training vectors, update the internal MiniBatchKMeans.\n        This method is specially designed to be used when data does not fit in memory.\n\n        :param x: Training vectors with shape=(N, D)\n        \"\"\"\n        assert x.ndim == 2\n        if self.kmeans:\n            self.kmeans.partial_fit(x)\n        else:\n            from sklearn.cluster import MiniBatchKMeans\n\n            self.kmeans = MiniBatchKMeans(\n                n_clusters=self.n_clusters, max_iter=self.iter\n            )\n            self.kmeans.partial_fit(x)\n\n    def build_codebook(self):\n        \"\"\"Constructs a codebook from the current MiniBatchKmeans\n        This step is not necessary if full KMeans is trained used calling `.fit`.\n        \"\"\"\n        self._codebook = self.kmeans.cluster_centers_\n        self._is_trained = True\n\n    def encode(self, x: 'np.ndarray'):\n        \"\"\"Encodes each row of the input array `x` it's closest cluster id.\"\"\"\n        self._check_trained()\n        assert x.dtype == np.float32\n        assert x.ndim == 2\n\n        codes, _ = vq(x, self.codebook)\n        return codes\n\n    def decode(self, x: 'np.ndarray'):\n        return None\n\n    @property\n    def codebook(self):\n        self._check_trained()\n        return self._codebook\n"
  },
  {
    "path": "annlite/core/index/__init__.py",
    "content": ""
  },
  {
    "path": "annlite/core/index/base.py",
    "content": "import abc\nfrom typing import List, Optional, Union\n\nimport numpy as np\n\nfrom ...enums import ExpandMode, Metric\nfrom ...helper import str2dtype\n\n\nclass BaseIndex(abc.ABC):\n    def __init__(\n        self,\n        dim: int,\n        dtype: Union[np.dtype, str] = np.float32,\n        metric: Metric = Metric.COSINE,\n        initial_size: Optional[int] = None,\n        expand_step_size: int = 10240,\n        expand_mode: ExpandMode = ExpandMode.STEP,\n        *args,\n        **kwargs\n    ):\n        assert expand_step_size > 0\n        self.initial_size = initial_size or expand_step_size\n\n        self.expand_step_size = expand_step_size\n        self.expand_mode = expand_mode\n\n        self.dim = dim\n        self.dtype = str2dtype(dtype) if isinstance(dtype, str) else dtype\n        self.metric = metric\n\n        self._size = 0\n        self._capacity = self.initial_size\n\n    @property\n    def capacity(self) -> int:\n        return self._capacity\n\n    @property\n    def size(self):\n        return self._size\n\n    @abc.abstractmethod\n    def add_with_ids(self, x: np.ndarray, ids: List[int], **kwargs):\n        ...\n\n    @abc.abstractmethod\n    def delete(self, ids: List[int]):\n        ...\n\n    @abc.abstractmethod\n    def update_with_ids(self, x: np.ndarray, ids: List[int], **kwargs):\n        ...\n\n    def reset(self, capacity: Optional[int] = None):\n        self._size = 0\n        self._capacity = capacity or self.initial_size\n"
  },
  {
    "path": "annlite/core/index/flat_index.py",
    "content": "from typing import List, Optional\n\nimport numpy as np\nfrom loguru import logger\n\nfrom ...math import cdist, top_k\nfrom .base import BaseIndex\n\n\nclass FlatIndex(BaseIndex):\n    def __init__(self, *args, **kwargs):\n        super(FlatIndex, self).__init__(*args, **kwargs)\n        self._data = np.zeros((self.initial_size, self.dim), dtype=self.dtype)\n\n    def search(\n        self, x: np.ndarray, limit: int = 10, indices: Optional[np.ndarray] = None\n    ):\n        _dim = x.shape[-1]\n        assert (\n            _dim == self.dim\n        ), f'the query embedding dimension does not match with index dimension: {_dim} vs {self.dim}'\n\n        x = x.reshape((-1, self.dim))\n\n        data = self._data[: self.size]\n        data_ids = np.arange(self.size)\n\n        if indices is not None:\n            data = self._data[indices]\n            data_ids = data_ids[indices]\n\n        dists = cdist(x, data, metric=self.metric.name.lower())\n        dists, idx = top_k(dists, limit, descending=False)\n\n        # TODO: change the shape of return\n        dists = dists[0]\n        data_ids = data_ids[idx[0]]\n\n        return dists, data_ids\n\n    def add_with_ids(self, x: np.ndarray, ids: List[int]):\n        for idx in ids:\n            if idx >= self._capacity:\n                self._expand_capacity()\n\n        start = self._size\n        end = start + len(x)\n\n        self._data[ids, :] = x\n        self._size = end\n\n    def _expand_capacity(self):\n        new_block = np.zeros((self.expand_step_size, self.dim), dtype=self.dtype)\n        self._data = np.concatenate((self._data, new_block), axis=0)\n\n        self._capacity += self.expand_step_size\n        logger.debug(\n            f'total storage capacity is expanded by {self.expand_step_size}',\n        )\n\n    def reset(self, capacity: Optional[int] = None):\n        super().reset(capacity=capacity)\n        self._data = np.zeros((self.capacity, self.dim), dtype=self.dtype)\n\n    def delete(self, ids: List[int]):\n        raise RuntimeError(\n            f'the deletion operation is not allowed for {self.__class__.__name__}!'\n        )\n\n    def update_with_ids(self, x: np.ndarray, ids: List[int], **kwargs):\n        self._data[ids, :] = x\n"
  },
  {
    "path": "annlite/core/index/hnsw/__init__.py",
    "content": "from .index import HnswIndex\n"
  },
  {
    "path": "annlite/core/index/hnsw/index.py",
    "content": "import math\nimport os.path\nfrom functools import wraps\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, List, Optional, Union\n\nimport numpy as np\nfrom loguru import logger\n\nfrom annlite.hnsw_bind import Index\n\nfrom ....enums import Metric\nfrom ....math import l2_normalize\nfrom ..base import BaseIndex\n\nif TYPE_CHECKING:\n    from ...codec.base import BaseCodec\n\n\ndef pre_process(f):\n    @wraps(f)\n    def pre_processed(self: 'HnswIndex', x: np.ndarray, *args, **kwargs):\n        if x.ndim == 1:\n            x = x.reshape((1, -1))\n        if x.dtype != self.dtype:\n            x = x.astype(self.dtype)\n\n        if self.normalization_enable:\n            x = l2_normalize(x)\n\n        if self.pq_enable:\n            if not self.pq_codec.is_trained:\n                raise RuntimeError(\n                    'Please train the PQ before using HNSW quantization backend'\n                )\n            elif not self._set_backend_pq:\n                self._index.loadPQ(self.pq_codec)\n                self._set_backend_pq = True\n            kwargs['pre_process_dtables'] = self.pq_codec.get_dist_mat(x)\n            x = self.pq_codec.encode(x)\n\n            assert kwargs['pre_process_dtables'].dtype == 'float32'\n            assert kwargs['pre_process_dtables'].flags['C_CONTIGUOUS']\n            return f(self, x, *args, **kwargs)\n        else:\n            return f(self, x, *args, **kwargs)\n\n    return pre_processed\n\n\nclass HnswIndex(BaseIndex):\n    def __init__(\n        self,\n        dim: int,\n        dtype: np.dtype = np.float32,\n        metric: Metric = Metric.COSINE,\n        ef_construction: int = 200,\n        ef_search: int = 50,\n        max_connection: int = 16,\n        pq_codec: Optional['BaseCodec'] = None,\n        index_file: Optional[Union[str, Path]] = None,\n        **kwargs,\n    ):\n        \"\"\"\n        :param dim: The dimensionality of vectors to index\n        :param index_file: A file-like object or a string containing a file name.\n        :param metric: Distance metric type, can be 'euclidean', 'inner_product', or 'cosine'\n        :param ef_construction: the size of the dynamic list for the nearest neighbors (used during the building).\n        :param ef_search: the size of the dynamic list for the nearest neighbors (used during the search).\n        :param max_connection: The number of bi-directional links created for every new element during construction.\n                    Reasonable range for M is 2-100.\n        \"\"\"\n        super().__init__(dim, dtype=dtype, metric=metric, **kwargs)\n\n        self.ef_construction = ef_construction\n        self.ef_search = ef_search\n        self.max_connection = max_connection\n        self.pq_codec = pq_codec\n        self._set_backend_pq = False\n        self.index_file = index_file\n\n        self._init_hnsw_index()\n\n    def _init_hnsw_index(self):\n        self._index = Index(space=self.space_name, dim=self.dim)\n        if self.index_file:\n            if os.path.exists(self.index_file):\n                logger.info(\n                    f'indexer will be loaded from {self.index_file}',\n                )\n                self.load(self.index_file)\n            else:\n                raise FileNotFoundError(\n                    f'index path: {self.index_file} does not exist',\n                )\n        else:\n            if self.pq_codec is not None and self.pq_codec.is_trained:\n                self._index.init_index(\n                    max_elements=self.capacity,\n                    ef_construction=self.ef_construction,\n                    M=self.max_connection,\n                    pq_codec=self.pq_codec,\n                )\n                self._set_backend_pq = True\n            else:\n                self._index.init_index(\n                    max_elements=self.capacity,\n                    ef_construction=self.ef_construction,\n                    M=self.max_connection,\n                    pq_codec=None,\n                )\n                self._set_backend_pq = False\n\n        self._index.set_ef(self.ef_search)\n\n    def load(self, index_file: Union[str, Path]):\n        self._index.load_index(str(index_file))\n        if self.pq_codec:\n            self._index.loadPQ(self.pq_codec)\n\n    def dump(self, index_file: Union[str, Path]):\n        self._index.save_index(str(index_file))\n\n    @pre_process\n    def add_with_ids(\n        self,\n        x: 'np.ndarray',\n        ids: List[int],\n        # kwargs maybe used by pre_process\n        pre_process_dtables=None,\n    ):\n        max_id = max(ids) + 1\n        if max_id > self.capacity:\n            expand_steps = math.ceil(max_id / self.expand_step_size)\n            self._expand_capacity(expand_steps * self.expand_step_size)\n\n        self._index.add_items(x, ids=ids, dtables=pre_process_dtables)\n\n    @pre_process\n    def search(\n        self,\n        query: 'np.ndarray',\n        limit: int = 10,\n        indices: Optional['np.ndarray'] = None,\n        # kwargs maybe used by pre_process\n        pre_process_dtables=None,\n    ):\n        ef_search = max(self.ef_search, limit)\n        self._index.set_ef(ef_search)\n\n        if indices is not None:\n            # TODO: add a smart strategy to speed up this case (bruteforce search would be better)\n            if len(indices) < limit:\n                limit = len(indices)\n            ids, dists = self._index.knn_query_with_filter(\n                query, filters=indices, k=limit, dtables=pre_process_dtables\n            )\n        else:\n            ids, dists = self._index.knn_query(\n                query, k=limit, dtables=pre_process_dtables\n            )\n\n        # convert squared l2 into euclidean distance\n        if self.metric == Metric.EUCLIDEAN:\n            dists = np.sqrt(dists)\n\n        return dists[0], ids[0]\n\n    def delete(self, ids: List[int]):\n        for i in ids:\n            self._index.mark_deleted(i)\n\n    def update_with_ids(self, x: 'np.ndarray', ids: List[int], **kwargs):\n        raise RuntimeError(\n            f'the update operation is not allowed for {self.__class__.__name__}!'\n        )\n\n    def _expand_capacity(self, new_capacity: int):\n        self._capacity = new_capacity\n        self._index.resize_index(new_capacity)\n        logger.debug(\n            f'HNSW index capacity is expanded by {self.expand_step_size}',\n        )\n\n    def reset(self, capacity: Optional[int] = None):\n        super().reset(capacity=capacity)\n        self._init_hnsw_index()\n\n    @property\n    def size(self):\n        return self._index.element_count\n\n    @property\n    def space_name(self):\n        if self.metric == Metric.EUCLIDEAN:\n            return 'l2'\n        elif self.metric == Metric.INNER_PRODUCT:\n            return 'ip'\n        return 'cosine'\n\n    @property\n    def pq_enable(self):\n        return self.pq_codec is not None\n\n    @property\n    def normalization_enable(self):\n        return self.metric == Metric.COSINE\n"
  },
  {
    "path": "annlite/core/index/pq_index.py",
    "content": "from typing import List, Optional\n\nimport numpy as np\n\nfrom ...math import top_k\nfrom ..codec.pq import PQCodec\nfrom .flat_index import FlatIndex\n\n\n# TODO: deprecated this index\nclass PQIndex(FlatIndex):  # pragma: no cover\n    def __init__(\n        self,\n        dim: int,\n        pq_codec: PQCodec,\n        **kwargs,\n    ):\n        assert pq_codec is not None\n        self._dense_dim = dim\n        super(PQIndex, self).__init__(\n            pq_codec.n_subvectors, dtype=pq_codec.code_dtype, **kwargs\n        )\n        self._pq_codec = pq_codec\n\n    def add_with_ids(self, x: np.ndarray, ids: List[int]):\n        x = self._pq_codec.encode(x)\n        super(PQIndex, self).add_with_ids(x, ids)\n\n    def search(\n        self, x: np.ndarray, limit: int = 10, indices: Optional[np.ndarray] = None\n    ):\n        _dim = x.shape[-1]\n        assert (\n            _dim == self._pq_codec.dim\n        ), f'the query embedding dimension does not match with index dimension: {_dim} vs {self.dim}'\n\n        precomputed = self._pq_codec.precompute_adc(x)\n\n        codes = self._data\n        data_idx = np.arange(self._capacity)\n\n        if indices is not None:\n            codes = self._data[indices]\n            data_idx = data_idx[indices]\n\n        dists = precomputed.adist(codes)  # (10000, )\n        dists = np.expand_dims(dists, axis=0)\n\n        dists, ids = top_k(dists, limit, descending=False)\n\n        # TODO: change the shape of return\n        ids = ids[0]\n        if indices is not None:\n            ids = data_idx[ids]\n\n        return dists[0], ids\n"
  },
  {
    "path": "annlite/enums.py",
    "content": "from enum import IntEnum\n\n\nclass BetterEnum(IntEnum):\n    \"\"\"The base class of Enum.\"\"\"\n\n    def __str__(self):\n        return self.name\n\n    @classmethod\n    def from_string(cls, s: str):\n        \"\"\"\n        Parse the enum from a string.\n        :param s: string representation of the enum value\n        :return: enum value\n        \"\"\"\n        try:\n            return cls[s.upper()]\n        except KeyError:\n            raise ValueError(\n                f'{s.upper()} is not a valid enum for {cls!r}, must be one of {list(cls)}'\n            )\n\n\nclass Metric(BetterEnum):\n    EUCLIDEAN = 1\n    INNER_PRODUCT = 2\n    COSINE = 3\n\n\nclass ExpandMode(BetterEnum):\n    STEP = 1\n    DOUBLE = 2\n    ADAPTIVE = 3\n"
  },
  {
    "path": "annlite/executor.py",
    "content": "import threading\nimport time\nimport traceback\nimport warnings\nfrom threading import Thread\nfrom typing import Dict, List, Optional, Tuple, Union\n\nfrom docarray import Document, DocumentArray\nfrom jina import Executor, requests\nfrom jina.logging.logger import JinaLogger\n\nINDEX_BATCH_SIZE = 1024\n\n\nclass AnnLiteIndexer(Executor):\n    \"\"\"A simple indexer that wraps the AnnLite indexer and adds a simple interface for indexing and searching.\n\n    :param n_dim: Dimensionality of vectors to index\n    :param metric: Distance metric type. Can be 'euclidean', 'inner_product', or 'cosine'\n    :param limit: Number of results to get for each query document in search\n    :param n_components: Number of components to use for dimensionality reduction\n    :param match_args: the arguments to `DocumentArray`'s match function\n    :param data_path: the workspace of the AnnLiteIndexer but not support when shards > 1.\n    :param ef_construction: The construction time/accuracy trade-off\n    :param ef_search: The query time accuracy/speed trade-off\n    :param max_connection: The maximum number of outgoing connections in the\n        graph (the \"M\" parameter)\n    :param include_metadata: If True, return the document metadata in response\n    :param index_access_paths: Default traversal paths on docs\n            (used for indexing, delete and update), e.g. '@r', '@c', '@r,c'\n    :param search_access_paths: Default traversal paths on docs\n    (used for search), e.g. '@r', '@c', '@r,c'\n    :param columns: A list or dict of column names to index.\n    :param dim: Deprecated, use n_dim instead\n    \"\"\"\n\n    def __init__(\n        self,\n        n_dim: int = 0,\n        metric: str = 'cosine',\n        limit: int = 10,\n        n_components: Optional[int] = None,\n        match_args: Optional[Dict] = None,\n        data_path: Optional[str] = None,\n        ef_construction: Optional[int] = None,\n        ef_search: Optional[int] = None,\n        max_connection: Optional[int] = None,\n        include_metadata: bool = True,\n        index_access_paths: str = '@r',\n        search_access_paths: str = '@r',\n        columns: Optional[Union[List[Tuple[str, str]], Dict[str, str]]] = None,\n        list_like: Optional[bool] = False,\n        dim: int = None,\n        *args,\n        **kwargs,\n    ):\n\n        super().__init__(*args, **kwargs)\n        self.logger = JinaLogger(self.__class__.__name__)\n\n        n_dim = n_dim or dim\n        if not n_dim:\n            raise ValueError('Please specify the dimension of the vectors to index!')\n\n        self.n_components = n_components\n        self.metric = metric\n        self.match_args = match_args or {}\n        self.include_metadata = include_metadata\n        if limit:\n            self.match_args.update({'limit': limit})\n\n        self.index_access_paths = index_access_paths\n        if 'index_traversal_paths' in kwargs:\n            warnings.warn(\n                f'`index_traversal_paths` is deprecated. Use `index_access_paths` instead.'\n            )\n            self.index_access_paths = kwargs['index_traversal_paths']\n\n        self.search_access_paths = search_access_paths\n        if 'search_traversal_paths' in kwargs:\n            warnings.warn(\n                f'`search_traversal_paths` is deprecated. Use `search_access_paths` instead.'\n            )\n            self.search_access_paths = kwargs['search_traversal_paths']\n\n        self._data_buffer = DocumentArray()\n        self._index_batch_size = INDEX_BATCH_SIZE\n        self._max_length_queue = 2 * self._index_batch_size\n        self._index_lock = threading.Lock()\n\n        self.logger = JinaLogger(getattr(self.metas, 'name', self.__class__.__name__))\n\n        if getattr(self.runtime_args, 'shards', 1) > 1 and data_path:\n            raise ValueError(\n                '`data_path` is not supported when shards > 1, please use `workspace` instead'\n            )\n\n        config = {\n            'n_dim': n_dim,\n            'n_components': n_components,\n            'metric': metric,\n            'ef_construction': ef_construction,\n            'ef_search': ef_search,\n            'max_connection': max_connection,\n            'data_path': data_path or self.workspace or './workspace',\n            'columns': columns,\n            'list_like': list_like,\n        }\n        self._index = DocumentArray(storage='annlite', config=config)\n\n        # start indexing thread in background to group indexing requests\n        # together and perform batch indexing at once\n        self._start_index_loop()\n\n    @requests(on='/index')\n    def index(\n        self, docs: Optional[DocumentArray] = None, parameters: dict = {}, **kwargs\n    ):\n        \"\"\"Index new documents\n\n        :param docs: the Documents to index\n        :param parameters: dictionary with options for indexing\n        Keys accepted:\n            - 'access_paths': traversal paths on docs, e.g. '@r', '@c', '@r,c'\n        \"\"\"\n\n        if not docs:\n            return\n\n        access_paths = parameters.get('access_paths', self.index_access_paths)\n        flat_docs = docs[access_paths]\n        if len(flat_docs) == 0:\n            return\n\n        while len(self._data_buffer) >= self._max_length_queue:\n            time.sleep(0.001)\n\n        with self._index_lock:\n            self._data_buffer.extend(flat_docs)\n\n    def _start_index_loop(self):\n        \"\"\"Start the indexing loop in background.\n\n        This loop is responsible for batch indexing the documents in the buffer.\n        \"\"\"\n\n        def _index_loop():\n            try:\n                while True:\n                    # if the buffer is none, will break the loop\n                    if self._data_buffer is None:\n                        break\n\n                    # if the buffer is empty, will wait for new documents to be added\n                    if len(self._data_buffer) == 0:\n                        time.sleep(0.1)  # sleep for 100ms\n                        continue\n\n                    # acquire the lock to prevent threading issues\n                    with self._index_lock:\n                        batch_docs = self._data_buffer.pop(\n                            range(\n                                self._index_batch_size\n                                if len(self._data_buffer) > self._index_batch_size\n                                else len(self._data_buffer)\n                            )\n                        )\n                        self._index.extend(batch_docs)\n                        self.logger.debug(f'indexing {len(batch_docs)} docs done...')\n            except Exception as e:\n                self.logger.error(traceback.format_exc())\n                raise e\n\n        self._index_thread = Thread(target=_index_loop, daemon=False)\n        self._index_thread.start()\n\n    @requests(on='/update')\n    def update(\n        self, docs: Optional[DocumentArray] = None, parameters: dict = {}, **kwargs\n    ):\n        \"\"\"Update existing documents\n\n        :param docs: the Documents to update\n        :param parameters: dictionary with options for updating\n        Keys accepted:\n            - 'access_paths': traversal paths on docs, e.g. '@r', '@c', '@r,c'\n            - 'raise_errors_on_not_found': if True, raise an error if a document is not found. Default is False.\n        \"\"\"\n\n        if not docs:\n            return\n\n        access_paths = parameters.get('access_paths', self.index_access_paths)\n        raise_errors_on_not_found = parameters.get('raise_errors_on_not_found', False)\n        flat_docs = docs[access_paths]\n        if len(flat_docs) == 0:\n            return\n\n        with self._index_lock:\n            if len(self._data_buffer) > 0:\n                raise RuntimeError(\n                    f'Cannot update documents while the pending documents in the buffer are not indexed yet. '\n                    'Please wait for the pending documents to be indexed.'\n                )\n            for doc in flat_docs:\n                try:\n                    self._index[doc.id] = doc\n                except IndexError:\n                    if raise_errors_on_not_found:\n                        raise Exception(\n                            f'The document (id={doc.id}) cannot be updated as'\n                            f'it is not found in the index'\n                        )\n                    else:\n                        self.logger.warning(\n                            f'cannot update doc {doc.id} as it does not exist in storage'\n                        )\n\n    @requests(on='/delete')\n    def delete(self, parameters: dict = {}, **kwargs):\n        \"\"\"Delete existing documents\n\n        Delete entries from the index by id\n        :param parameters: parameters to the request\n        \"\"\"\n\n        delete_ids = parameters.get('ids', [])\n        if len(delete_ids) == 0:\n            return\n\n        with self._index_lock:\n            if len(self._data_buffer) > 0:\n                raise RuntimeError(\n                    f'Cannot delete documents while the pending documents in the buffer are not indexed yet. '\n                    'Please wait for the pending documents to be indexed.'\n                )\n\n            del self._index[delete_ids]\n\n    @requests(on='/search')\n    def search(\n        self, docs: Optional[DocumentArray] = None, parameters: dict = {}, **kwargs\n    ):\n        \"\"\"Perform a vector similarity search and retrieve Document matches\n\n        Search can be performed with candidate filtering. Filters are a triplet (column,operator,value).\n        More than a filter can be applied during search. Therefore, conditions for a filter are specified as a list triplets.\n        Each triplet contains:\n\n        - column: Column used to filter.\n        - operator: Binary operation between two values. Some supported operators include `['>','<','=','<=','>=']`.\n        - value: value used to compare a candidate.\n\n        :param docs: the Documents to search with\n        :param parameters: dictionary for parameters for the search operation\n        Keys accepted:\n            - 'access_paths' (str): traversal paths on docs, e.g. '@r', '@c', '@r,c'\n            - 'filter' (dict): the filtering conditions on document tags\n            - 'limit' (int): nr of matches to get per Document\n        \"\"\"\n\n        if not docs:\n            return\n\n        access_paths = parameters.get('access_paths', self.search_access_paths)\n        flat_docs = docs[access_paths]\n        match_args = (\n            {**self.match_args, **parameters}\n            if parameters is not None\n            else self.match_args\n        )\n\n        with self._index_lock:\n            # if len(self._data_buffer) > 0:\n            #     raise RuntimeError(\n            #         f'Cannot search documents while the pending documents in the buffer are not indexed yet. '\n            #         'Please wait for the pending documents to be indexed.'\n            #     )\n\n            flat_docs.match(self._index, **match_args)\n\n    @requests(on='/backup')\n    def backup(self, parameters: Optional[Dict] = {}, **kwargs):\n        \"\"\"\n        Backup data to local or remote.\n        Use api of <class 'annlite.index.AnnLite'>\n\n        Keys accepted:\n            - 'target' (str): the name of indexer you want to backup as\n        \"\"\"\n\n        target_name = parameters.get('target_name', None)\n        token = parameters.get('token', None)\n        if target_name:\n            target_name = f'{target_name}_{self.runtime_args.shard_id}'\n        with self._index_lock:\n            if len(self._data_buffer) > 0:\n                raise RuntimeError(\n                    f'Cannot backup documents while the pending documents in the buffer are not indexed yet. '\n                    'Please wait for the pending documents to be indexed.'\n                )\n            self._index._annlite.backup(target_name, token)\n        if self._index._list_like:\n            self._index._save_offset2ids()\n\n    @requests(on='/restore')\n    def restore(self, parameters: Optional[Dict] = {}, **kwargs):\n        \"\"\"\n        Restore data from local or remote.\n        Use api of <class 'annlite.index.AnnLite'>\n        \"\"\"\n        source_name = parameters.get('source_name', None)\n        token = parameters.get('token', None)\n        if source_name:\n            source_name = f'{source_name}_{self.runtime_args.shard_id}'\n        self._index._annlite.restore(source_name, token)\n        if self._index._list_like:\n            self._index._load_offset2ids()\n\n    @requests(on='/filter')\n    def filter(self, parameters: Dict, **kwargs):\n        \"\"\"\n        Query documents from the indexer by the filter `query` object in parameters. The `query` object must follow the\n        specifications in the `find` method of `DocumentArray` using annlite: https://docarray.jina.ai/fundamentals/documentarray/find/#filter-with-query-operators\n        :param parameters: Dictionary to define the `filter` that you want to use.\n        \"\"\"\n\n        return self._index.find(parameters.get('filter', None))\n\n    @requests(on='/fill_embedding')\n    def fill_embedding(self, docs: DocumentArray, **kwargs):\n        \"\"\"\n        retrieve embedding of Documents by id\n        :param docs: DocumentArray to search with\n        \"\"\"\n\n        for doc in docs:\n            doc.embedding = self._index[doc.id].embedding\n\n    @requests(on='/status')\n    def status(self, **kwargs) -> DocumentArray:\n        \"\"\"Return the document containing status information about the indexer.\n\n        The status will contain information on the total number of indexed and deleted\n        documents, and on the number of (searchable) documents currently in the index.\n        \"\"\"\n\n        status = Document(\n            tags={\n                'appending_size': len(self._data_buffer),\n                'total_docs': len(self._index),\n                'index_size': len(self._index),\n            }\n        )\n        return DocumentArray([status])\n\n    def flush(self):\n        \"\"\"Flush all the data in the buffer to the index\"\"\"\n        while len(self._data_buffer) > 0:\n            time.sleep(0.1)\n\n    @requests(on='/clear')\n    def clear(self, **kwargs):\n        \"\"\"Clear the index of all entries.\"\"\"\n        self.flush()\n\n        with self._index_lock:\n            self._data_buffer = None\n            self._index_thread.join()\n\n        self._data_buffer = DocumentArray()\n        self._index.clear()\n\n        self._start_index_loop()\n\n    def close(self, **kwargs):\n        \"\"\"Close the index.\"\"\"\n        super().close()\n\n        self.flush()\n\n        # wait for the index thread to finish\n        with self._index_lock:\n            self._data_buffer = None\n            self._index_thread.join()\n\n        # WARNING: the commented code below hangs the close in pytest `pytest tests/test_*.py`\n        # But don't know why. It works fine in `pytest tests/test_executor.py` and normal python execution\n        del self._index\n"
  },
  {
    "path": "annlite/filter.py",
    "content": "from typing import Dict\n\nLOGICAL_OPERATORS = {'$and': 'AND', '$or': 'OR'}\n\nCOMPARISON_OPERATORS = {\n    '$lt': '<',\n    '$gt': '>',\n    '$lte': '<=',\n    '$gte': '>=',\n    '$eq': '=',\n    '$neq': '!=',\n}\n\nMEMBERSHIP_OPERATORS = {'$in': 'IN', '$nin': 'NOT IN'}\n\n\ndef _sql_parsing(data, default_logic: str = 'AND'):\n    \"\"\"\n    :param data: JSON Object (dict).\n    :param parameters: dict.\n    :return: where clause (str) built from data\n    \"\"\"\n    where_clause = ''\n    parameters = []\n\n    if isinstance(data, dict):\n        for i, (key, value) in enumerate(data.items()):\n            if key in LOGICAL_OPERATORS:\n                clause, params = _sql_parsing(\n                    value, default_logic=LOGICAL_OPERATORS[key]\n                )\n                if i == 0:\n                    where_clause += clause\n                else:\n                    where_clause += f' {LOGICAL_OPERATORS[key]} {clause}'\n                parameters.extend(params)\n            elif key.startswith('$'):\n                raise ValueError(\n                    f'The operator {key} is not supported yet, please double check the given filters!'\n                )\n            else:\n                if i > 0:\n                    where_clause += f' {default_logic} '\n\n                items = list(value.items())\n\n                if len(items) == 0:\n                    raise ValueError(f'The query express is illegal: {data}')\n                elif len(items) > 1:\n                    clause_list, params_list = [], []\n\n                    for op, val in items:\n                        _clause, _params = _sql_parsing({key: {op: val}})\n                        clause_list.append(_clause)\n                        params_list.extend(_params)\n\n                    where_clause += f' AND '.join(clause_list)\n                    parameters.extend(params_list)\n                else:\n                    op, val = items[0]\n                    if op in LOGICAL_OPERATORS:\n                        clause, params = _sql_parsing(\n                            val, default_logic=LOGICAL_OPERATORS[op]\n                        )\n                        where_clause += clause\n                        parameters.extend(params)\n                    elif op in COMPARISON_OPERATORS:\n                        parameters.append(val)\n                        where_clause += f'({key} {COMPARISON_OPERATORS[op]} ?)'\n                    elif op in MEMBERSHIP_OPERATORS:\n                        parameters.extend(val)\n                        where_clause += f'({key} {MEMBERSHIP_OPERATORS[op]}({\", \".join([\"?\"]*len(val))}))'\n                    else:\n                        raise ValueError(\n                            f'The operator {op} is not supported yet, please double check the given filters!'\n                        )\n    elif isinstance(data, list):\n        clause_list, params_list = [], []\n        for d in data:\n            _clause, _params = _sql_parsing(d)\n            clause_list.append(_clause)\n            params_list.extend(_params)\n        where_clause += '(' + f' {default_logic} '.join(clause_list) + ')'\n        parameters.extend(params_list)\n\n    elif isinstance(data, str):\n        return data, parameters\n    else:\n        raise ValueError(f'The query express is illegal: {data}')\n    return where_clause, tuple(parameters)\n\n\nclass Filter(object):\n    \"\"\"A class to parse query language to SQL where clause.\"\"\"\n\n    def __init__(self, tree_data: Dict = {}):\n        self.tree_data = tree_data\n\n    def parse_where_clause(self):\n        return _sql_parsing(self.tree_data or {})\n"
  },
  {
    "path": "annlite/helper.py",
    "content": "import sys\n\nimport numpy as np\nfrom loguru import logger\n\n\ndef setup_logging(debug: bool):\n    \"\"\"\n    Setup the log formatter for AnnLite.\n    \"\"\"\n\n    log_level = 'INFO'\n    if debug:\n        log_level = 'DEBUG'\n\n    logger.remove()\n    logger.add(\n        sys.stdout,\n        colorize=True,\n        level=log_level,\n    )\n\n\ndef str2dtype(dtype_str: str):\n    if dtype_str in ['double', 'float64']:\n        dtype = np.float64\n    elif dtype_str in ['half', 'float16']:\n        dtype = np.float16\n    elif dtype_str in ['float', 'float32']:\n        dtype = np.float32\n    elif dtype_str in ['bfloat16']:\n        dtype = np.bfloat16\n    elif dtype_str in ['long', 'int64']:\n        dtype = np.int64\n    elif dtype_str in ['int', 'int32']:\n        dtype = np.int32\n    elif dtype_str in ['int16']:\n        dtype = np.int16\n    elif dtype_str in ['int8']:\n        dtype = np.int8\n    elif dtype_str in ['uint8']:\n        dtype = np.uint8\n    elif dtype_str in ['bool']:\n        dtype = np.bool\n    else:\n        raise TypeError(f'Unrecognized dtype string: {dtype_str}')\n    return dtype\n"
  },
  {
    "path": "annlite/hubble_tools.py",
    "content": "import os\nimport platform\nimport shutil\nimport time\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nfrom filesplit.merge import Merge\nfrom filesplit.split import Split\nfrom loguru import logger\n\nignored_extn = ['.DS_Store']\n\n\ndef get_size(input: Path) -> float:\n    import os\n\n    return os.stat(str(input)).st_size / (1024 * 1024)\n\n\ndef make_archive(input: Path, output_name: str) -> Path:\n    \"\"\"\n    This function will create a zip archive of the input file (tmp.zip) at the\n    same folder of input path.\n    \"\"\"\n    output_path = shutil.make_archive(\n        os.path.join(str(input.parent), output_name),\n        'zip',\n        str(input.parent),\n        str(input.name),\n    )\n    return Path(output_path)\n\n\nclass Uploader:\n    def __init__(self, size_limit=1024, client=None):\n        \"\"\"\n        This class create a filesplit object to split the file into small pieces and\n        upload them on to hubble.\n        :params size_limit: The max size of split files.\n        :params client: hubble client used for uploading.\n        \"\"\"\n        self.size_limit = size_limit\n        self.client = client\n\n    def upload_file(\n        self, input: Path, target_name: str, type: str, cell_id: Union[int, str]\n    ):\n        logger.info(f'Start to upload single file: {input} to hubble ...')\n        size = get_size(input)\n        if size > self.size_limit:\n            split_list = self._split_file(input)\n            self.upload_directory(split_list, target_name, type, cell_id, merge=False)\n            shutil.rmtree(split_list)\n        else:\n            if self._check_exists(target_name, type, input.name):\n                return\n            self._upload_hubble(input, target_name, type, input.name, cell_id)\n\n    def upload_directory(\n        self,\n        input: Path,\n        target_name: str,\n        type: str,\n        cell_id: Union[int, str],\n        merge: bool = True,\n    ):\n        def _upload():\n            if self._check_exists(target_name, type, str(idx) + '.zip'):\n                return\n\n            Path.mkdir(input.parent / str(idx))\n            for f in split_list:\n                shutil.copy(f, input.parent / str(idx))\n            output_path = make_archive(input.parent / str(idx), str(idx) + '.zip')\n            self._upload_hubble(\n                output_path, target_name, type, str(idx) + '.zip', cell_id\n            )\n            Path(output_path).unlink()\n            shutil.rmtree(input.parent / str(idx))\n\n        logger.info(f'Start to upload directory: {input} to hubble ...')\n        if merge:\n            size_list = list(\n                zip(list(input.iterdir()), [get_size(f) for f in list(input.iterdir())])\n            )\n            sorted_size_list = sorted(size_list, key=lambda x: x[1])\n\n            split_list = []\n            total_size = 0\n            idx = 0\n\n            for file_name, file_size in sorted_size_list:\n                for extn in ignored_extn:\n                    if extn in str(file_name):\n                        continue\n                if total_size + file_size > self.size_limit:\n                    if len(split_list) == 0:\n                        raise Exception(\n                            f'The smallest file: {file_size} is bigger '\n                            f'than size_limit. Please set a larger value '\n                            f'of size_limit, now is {self.size_limit}MB.'\n                        )\n                    _upload()\n                    idx += 1\n                    total_size = 0\n                    split_list = [file_name]\n                else:\n                    split_list.append(file_name)\n                    total_size += file_size\n            if len(split_list) > 0:\n                _upload()\n        else:\n            for idx, file_name in enumerate(list(input.glob('*'))):\n                if self._check_exists(target_name, type, str(file_name.name)):\n                    continue\n                self._upload_hubble(\n                    file_name, target_name, type, str(file_name.name), cell_id\n                )\n\n    def archive_and_upload(\n        self,\n        target_name: str,\n        type: str,\n        file_name: str,\n        cell_id: Union[int, str],\n        root_path: Path,\n        upload_folder: str,\n    ):\n        if self._check_exists(target_name, type, file_name):\n            return\n        upload_file = shutil.make_archive(\n            os.path.join(str(root_path), f'{target_name}_{type}'),\n            'zip',\n            str(root_path),\n            upload_folder,\n        )\n\n        logger.info(\n            f'Start to upload: {upload_file} to hubble. '\n            f'[target_name: {target_name}, '\n            f'type: {type}, '\n            f'file_name: {file_name}, '\n            f'cell_id: {cell_id}].'\n        )\n\n        self.client.upload_artifact(\n            f=upload_file,\n            metadata={\n                'name': target_name,\n                'type': type,\n                'file_name': file_name,\n                'cell': cell_id,\n            },\n        )\n        Path(upload_file).unlink()\n\n    def _check_exists(self, target_name: str, type: str, file_name: str) -> bool:\n        art_list = self.client.list_artifacts(\n            filter={\n                'metaData.name': target_name,\n                'metaData.type': f'{type}',\n                'metaData.file_name': f'{file_name}',\n            }\n        )\n        if len(art_list['data']) != 0:\n            logger.info(\n                f'[target_name: {target_name}, type: {type}, file_name: {file_name}] '\n                f'already exists on hubble, will skip it ...'\n            )\n            return True\n        else:\n            return False\n\n    def _split_file(self, input: Path) -> Path:\n        output_dir = input / f'{input}_split'\n        if output_dir.exists():\n            logger.info(\n                f'Origin file: {str(input)} has already been split to: {output_dir}, will skip ...'\n            )\n            return output_dir\n        Path.mkdir(output_dir)\n        Split(str(input), str(output_dir)).bysize(size=self.size_limit * 1024 * 1024)\n        num_files = len(list(output_dir.glob('*')))\n        logger.info(\n            f'Origin file: {str(input)} has been split '\n            f'into {num_files} parts. Output file: {output_dir}'\n        )\n        return output_dir\n\n    def _upload_hubble(\n        self,\n        upload_file: Path,\n        target_name: str,\n        type: str,\n        file_name: str,\n        cell_id: Union[str, int],\n    ):\n\n        logger.info(\n            f'Start to upload: {upload_file} to hubble. '\n            f'[target_name: {target_name}, '\n            f'type: {type}, '\n            f'file_name: {file_name}, '\n            f'cell_id: {cell_id}].'\n        )\n\n        start_time = time.time()\n        failed_times = 0\n\n        while True:\n            try:\n                self.client.upload_artifact(\n                    f=str(upload_file),\n                    metadata={\n                        'name': target_name,\n                        'type': type,\n                        'file_name': file_name,\n                        'cell': cell_id,\n                    },\n                    show_progress=True,\n                )\n                break\n            except Exception as e:\n                logger.info(e)\n                failed_times += 1\n                if failed_times == 3:\n                    logger.info(\n                        f'Tried more than 3 times to upload {upload_file}, type is: {type}, will exist...'\n                    )\n                    return\n                else:\n                    continue\n\n        logger.info(\n            f'Takes {time.time() - start_time} seconds to upload {upload_file}.'\n        )\n\n\nclass Merger:\n    def __init__(self, restore_path, client):\n        \"\"\"\n        This class creates an object to download and merge the split files from hubble.\n        :param restore_path: tmp directory for downloading and merging files.\n        :param client: hubble client used for merging files.\n        \"\"\"\n        self.restore_path = restore_path\n        self.restore_path.mkdir(parents=True)\n        self.client = client\n\n    def merge_file(self, inputdir: Path, outputdir: Path, outputfilename: Path):\n        Merge(\n            inputdir=str(inputdir),\n            outputdir=str(outputdir),\n            outputfilename=str(outputfilename),\n        ).merge()\n\n    def get_artifact_ids(self, art_list, type: str, cell_id: Optional[int] = None):\n        ids = [\n            [\n                art['_id'],\n                art['metaData']['type'],\n                art['metaData']['file_name'],\n                art['metaData']['cell'],\n            ]\n            for art in art_list['data']\n            if type == art['metaData']['type']\n        ]\n        if cell_id:\n            ids = [item for item in ids if int(item[3]) == cell_id]\n        ids = [[item[0], item[1], item[2]] for item in ids]\n        return ids\n\n    def download(self, ids, download_folder):\n        Path.mkdir(self.restore_path / download_folder)\n\n        for ids, type, file_name in ids:\n            self.client.download_artifact(\n                id=ids,\n                f=str(self.restore_path / download_folder / file_name),\n                show_progress=True,\n            )\n"
  },
  {
    "path": "annlite/index.py",
    "content": "import hashlib\nimport logging\nimport os\nimport platform\nimport warnings\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Dict, List, Optional, Union\n\nimport numpy as np\nfrom docarray.math.ndarray import to_numpy_array\nfrom loguru import logger\n\nif TYPE_CHECKING:\n    from docarray import DocumentArray\n\nfrom .container import CellContainer\nfrom .core import PQCodec, ProjectorCodec, VQCodec\nfrom .enums import Metric\nfrom .filter import Filter\nfrom .helper import setup_logging\nfrom .math import cdist, top_k\n\nMAX_TRAINING_DATA_SIZE = 10240\n\n\nclass AnnLite(CellContainer):\n    \"\"\":class:`AnnLite` is an approximate nearest neighbor search library.\n\n    To create a :class:`AnnLite` object, simply:\n\n        .. highlight:: python\n        .. code-block:: python\n            ann = AnnLite(256, metric='cosine')\n\n    :param n_dim: dimensionality of input vectors. there are 2 constraints on dim:\n            (1) it needs to be divisible by n_subvectors; (2) it needs to be a multiple of 4.*\n    :param metric: distance metric type, can be 'euclidean', 'inner_product', or 'cosine'.\n    :param n_subvectors: number of sub-quantizers, essentially this is the byte size of\n            each quantized vector, default is None.\n    :param n_cells:  number of coarse quantizer clusters, default is 1.\n    :param n_probe: number of cells to search for each query, default is 16.\n    :param n_components: number of components to keep.\n    :param initial_size: initial capacity assigned to each voronoi cell of coarse quantizer.\n            ``n_cells * initial_size`` is the number of vectors that can be stored initially.\n            if any cell has reached its capacity, that cell will be automatically expanded.\n            If you need to add vectors frequently, a larger value for init_size is recommended.\n    :param columns: the columns to be indexed for fast filtering, default is None.\n    :param filterable_attrs: a dict of attributes to be indexed for fast filtering, default is None.\n            The key is the attribute name, and the value is the attribute type. And it only works when ``columns`` is None.\n    :param data_path: path to the directory where the data is stored.\n    :param create_if_missing: if False, do not create the directory path if it is missing.\n    :param read_only: if True, the index is not writable.\n    :param verbose: if True, will print the debug logging info.\n\n    .. note::\n        Remember that the shape of any tensor that contains data points has to be `[n_data, dim]`.\n    \"\"\"\n\n    def __init__(\n        self,\n        n_dim: int,\n        metric: Union[str, Metric] = 'cosine',\n        n_cells: int = 1,\n        n_subvectors: Optional[int] = None,\n        n_clusters: Optional[int] = 256,\n        n_probe: int = 16,\n        n_components: Optional[int] = None,\n        initial_size: Optional[int] = None,\n        expand_step_size: int = 10240,\n        columns: Optional[Union[Dict, List]] = None,\n        filterable_attrs: Optional[Dict] = None,\n        data_path: Union[Path, str] = Path('./data'),\n        create_if_missing: bool = True,\n        read_only: bool = False,\n        verbose: bool = False,\n        **kwargs,\n    ):\n        setup_logging(verbose)\n\n        if 'dim' in kwargs:\n            warnings.warn(\n                'The argument `dim` will be deprecated, please use `n_dim` instead.'\n            )\n            n_dim = kwargs['dim']\n\n        if n_subvectors:\n            assert (\n                n_dim % n_subvectors == 0\n            ), '\"n_dim\" needs to be divisible by \"n_subvectors\"'\n        self.n_dim = n_dim\n        self.n_components = n_components\n        self.n_subvectors = n_subvectors\n        self.n_clusters = n_clusters\n        self.n_probe = max(n_probe, n_cells)\n        self.n_cells = n_cells\n        self.size_limit = 2048\n\n        if isinstance(metric, str):\n            metric = Metric.from_string(metric)\n        self.metric = metric\n\n        self._use_smart_probing = True\n\n        self.read_only = read_only\n\n        data_path = Path(data_path)\n        if create_if_missing:\n            data_path.mkdir(parents=True, exist_ok=True)\n        self.data_path = data_path\n\n        self._projector_codec = None\n        if self._projector_codec_path.exists():\n            logger.info(\n                f'Load pre-trained projector codec (n_components={self.n_components}) from {self.model_path}'\n            )\n            self._projector_codec = ProjectorCodec.load(self._projector_codec_path)\n        elif n_components:\n            logger.info(\n                f'Initialize Projector codec (n_components={self.n_components})'\n            )\n            self._projector_codec = ProjectorCodec(\n                n_dim, n_components=self.n_components\n            )\n\n        self._vq_codec = None\n        if self._vq_codec_path.exists():\n            logger.info(\n                f'Load trained VQ codec (K={self.n_cells}) from {self.model_path}'\n            )\n            self._vq_codec = VQCodec.load(self._vq_codec_path)\n        elif n_cells > 1:\n            logger.info(f'Initialize VQ codec (K={self.n_cells})')\n            self._vq_codec = VQCodec(self.n_cells, metric=self.metric)\n\n        self._pq_codec = None\n        if self._pq_codec_path.exists():\n            logger.info(\n                f'Load trained PQ codec (n_subvectors={self.n_subvectors}) from {self.model_path}'\n            )\n            self._pq_codec = PQCodec.load(self._pq_codec_path)\n        elif n_subvectors:\n            logger.info(f'Initialize PQ codec (n_subvectors={self.n_subvectors})')\n            self._pq_codec = PQCodec(\n                dim=n_dim\n                if not self._projector_codec\n                else self._projector_codec.n_components,\n                n_subvectors=self.n_subvectors,\n                n_clusters=self.n_clusters,\n                metric=self.metric,\n            )\n\n        if columns is not None:\n            if filterable_attrs:\n                logger.warning('`filterable_attrs` will be overwritten by `columns`.')\n\n            filterable_attrs = {}\n            for n, t in columns.items() if isinstance(columns, dict) else columns:\n                filterable_attrs[n] = t\n\n        super(AnnLite, self).__init__(\n            n_dim,\n            metric=metric,\n            projector_codec=self._projector_codec,\n            pq_codec=self._pq_codec,\n            n_cells=n_cells,\n            initial_size=initial_size,\n            expand_step_size=expand_step_size,\n            filterable_attrs=filterable_attrs,\n            data_path=data_path,\n            **kwargs,\n        )\n\n        if not self.is_trained and self.total_docs > 0:\n            # train the index from scratch based on the data in the data_path\n            logger.info(f'Train the index by reading data from {self.data_path}')\n            total_size = 0\n            # TODO: add a progress bar\n            for docs in self.documents_generator(0, batch_size=1024):\n                x = to_numpy_array(docs.embeddings)\n                total_size += x.shape[0]\n                self.partial_train(x, auto_save=True, force_train=True)\n                if total_size >= MAX_TRAINING_DATA_SIZE:\n                    break\n            logger.info(f'Total training data size: {total_size}')\n\n        if self.total_docs > 0:\n            self.restore()\n\n    def _sanity_check(self, x: 'np.ndarray'):\n        assert x.ndim == 2, 'inputs must be a 2D array'\n        assert (\n            x.shape[1] == self.n_dim\n        ), f'inputs must have the same dimension as the index , got {x.shape[1]}, expected {self.n_dim}'\n\n        return x.shape\n\n    def train(self, x: 'np.ndarray', auto_save: bool = True, force_train: bool = False):\n        \"\"\"Train the index with the given data.\n\n        :param x: the ndarray data for training.\n        :param auto_save: if False, will not dump the trained model to ``model_path``.\n        :param force_train: if True, enforce to retrain the model, and overwrite the model if ``auto_save=True``.\n        \"\"\"\n        n_data, _ = self._sanity_check(x)\n\n        if self.is_trained and not force_train:\n            logger.warning(\n                'The indexer has been trained or is not trainable. Please use ``force_train=True`` to retrain.'\n            )\n            return\n\n        if self._projector_codec:\n            logger.info(\n                f'Start training Projector codec (n_components={self.n_components}) with {n_data} data...'\n            )\n            self._projector_codec.fit(x)\n\n        if self._vq_codec:\n            logger.info(\n                f'Start training VQ codec (K={self.n_cells}) with {n_data} data...'\n            )\n            self._vq_codec.fit(x)\n\n        if self._pq_codec:\n            logger.info(\n                f'Start training PQ codec (n_subvectors={self.n_subvectors}) with {n_data} data...'\n            )\n            self._pq_codec.fit(x)\n\n        logger.info(f'The annlite is successfully trained!')\n\n        if auto_save:\n            self.dump_model()\n\n    def partial_train(\n        self, x: np.ndarray, auto_save: bool = True, force_train: bool = False\n    ):\n        \"\"\"Partially train the index with the given data.\n\n        :param x: the ndarray data for training.\n        :param auto_save: if False, will not dump the trained model to ``model_path``.\n        :param force_train: if True, enforce to retrain the model, and overwrite the model if ``auto_save=True``.\n\n        \"\"\"\n        n_data, _ = self._sanity_check(x)\n\n        if self.is_trained and not force_train:\n            logger.warning(\n                'The annlite has been trained or is not trainable. Please use ``force_train=True`` to retrain.'\n            )\n            return\n\n        if self._projector_codec:\n            logging.info(\n                f'Partial training Projector codec (n_components={self.n_components}) with {n_data} data...'\n            )\n            self._projector_codec.partial_fit(x)\n\n        if self._vq_codec:\n            logger.info(\n                f'Partial training VQ codec (K={self.n_cells}) with {n_data} data...'\n            )\n            self._vq_codec.partial_fit(x)\n\n        if self._pq_codec:\n            logger.info(\n                f'Partial training PQ codec (n_subvectors={self.n_subvectors}) with {n_data} data...'\n            )\n            self._pq_codec.partial_fit(x)\n\n        if auto_save:\n            self.dump_model()\n\n    def index(self, docs: 'DocumentArray', **kwargs):\n        \"\"\"Add the documents to the index.\n\n        :param docs: the document array to be indexed.\n        \"\"\"\n\n        if self.read_only:\n            logger.error('The indexer is readonly, cannot add new documents')\n            return\n\n        if not self.is_trained:\n            raise RuntimeError(f'The indexer is not trained, cannot add new documents')\n\n        x = to_numpy_array(docs.embeddings)\n        n_data, _ = self._sanity_check(x)\n\n        assigned_cells = (\n            self._vq_codec.encode(x)\n            if self._vq_codec\n            else np.zeros(n_data, dtype=np.int64)\n        )\n        return super(AnnLite, self).insert(x, assigned_cells, docs)\n\n    def update(\n        self,\n        docs: 'DocumentArray',\n        raise_errors_on_not_found: bool = False,\n        insert_if_not_found: bool = True,\n        **kwargs,\n    ):\n        \"\"\"Update the documents in the index.\n\n        :param insert_if_not_found: whether to raise error when updated id is not found.\n        :param raise_errors_on_not_found: whether to raise exception when id not found.\n        :param docs: the document array to be updated.\n        \"\"\"\n        if self.read_only:\n            logger.error('The indexer is readonly, cannot update documents')\n            return\n\n        if not self.is_trained:\n            raise RuntimeError(f'The indexer is not trained, cannot add new documents')\n\n        x = to_numpy_array(docs.embeddings)\n        n_data, _ = self._sanity_check(x)\n\n        assigned_cells = (\n            self._vq_codec.encode(x)\n            if self._vq_codec\n            else np.zeros(n_data, dtype=np.int64)\n        )\n\n        return super(AnnLite, self).update(\n            x,\n            assigned_cells,\n            docs,\n            raise_errors_on_not_found=raise_errors_on_not_found,\n            insert_if_not_found=insert_if_not_found,\n        )\n\n    def search(\n        self,\n        docs: 'DocumentArray',\n        filter: Optional[dict] = None,\n        limit: int = 10,\n        include_metadata: bool = True,\n        **kwargs,\n    ):\n        \"\"\"Search the index, and attach matches to the query Documents in `docs`\n\n        :param docs: the document array to be searched.\n        :param filter: the filter to be applied to the search.\n        :param limit: the number of results to get for each query document in search\n        :param include_metadata: whether to return document metadata in response.\n        \"\"\"\n        if not self.is_trained:\n            raise RuntimeError(f'The indexer is not trained, cannot add new documents')\n\n        query_np = to_numpy_array(docs.embeddings)\n\n        match_dists, match_docs = self.search_by_vectors(\n            query_np, filter=filter, limit=limit, include_metadata=include_metadata\n        )\n\n        for doc, matches in zip(docs, match_docs):\n            doc.matches = matches\n\n    def search_by_vectors(\n        self,\n        query_np: 'np.ndarray',\n        filter: Optional[dict] = None,\n        limit: int = 10,\n        include_metadata: bool = True,\n    ):\n        \"\"\"Search the index by vectors, and return the matches.\n\n        :param query_np: the query vectors.\n        :param filter: the filter to be applied to the search.\n        :param limit: the number of results to get for each query document in search\n        :param include_metadata: whether to return document metadata in response.\n        \"\"\"\n\n        cells = self._cell_selection(query_np, limit)\n        where_clause, where_params = Filter(filter or {}).parse_where_clause()\n\n        match_dists, match_docs = self.search_cells(\n            query=query_np,\n            cells=cells,\n            where_clause=where_clause,\n            where_params=where_params,\n            limit=limit,\n            include_metadata=include_metadata,\n        )\n        return match_dists, match_docs\n\n    def filter(\n        self,\n        filter: Dict,\n        limit: int = 10,\n        offset: int = 0,\n        order_by: Optional[str] = None,\n        ascending: bool = True,\n        include_metadata: bool = True,\n    ):\n        \"\"\"Find the documents by the filter.\n\n        :param filter: the filter to be applied to the search.\n        :param limit: the number of results.\n        :param offset: the offset of the results.\n        :param order_by: the field to order the results.\n        :param ascending: whether to order the results in ascending order.\n        :param include_metadata: whether to return document metadata in response.\n        \"\"\"\n        cells = [x for x in range(self.n_cells)]\n        where_clause, where_params = Filter(filter or {}).parse_where_clause()\n\n        match_docs = self.filter_cells(\n            cells=cells,\n            where_clause=where_clause,\n            where_params=where_params,\n            limit=limit,\n            offset=offset,\n            order_by=order_by,\n            ascending=ascending,\n            include_metadata=include_metadata,\n        )\n        if limit > 0:\n            return match_docs[:limit]\n        return match_docs\n\n    def get_doc_by_id(self, doc_id: str):\n        \"\"\"Get the document by id.\n\n        :param doc_id: the document id.\n        \"\"\"\n\n        return self._get_doc_by_id(doc_id)\n\n    def get_docs(\n        self,\n        filter: Optional[dict] = None,\n        limit: int = 10,\n        offset: int = 0,\n        order_by: Optional[str] = None,\n        ascending: bool = True,\n    ):\n        \"\"\"Get the documents.\n\n        :param filter: the filter to be applied to the search.\n        :param limit: the number of results.\n        :param offset: the offset of the results.\n        :param order_by: the field to order the results.\n        :param ascending: whether to order the results in ascending order. It only works when `order_by` is specified.\n        \"\"\"\n\n        return self.filter(\n            filter=filter,\n            limit=limit,\n            offset=offset,\n            order_by=order_by,\n            ascending=ascending,\n            include_metadata=True,\n        )\n\n    def _cell_selection(self, query_np, limit):\n        n_data, _ = self._sanity_check(query_np)\n\n        if self._vq_codec:\n            dists = cdist(\n                query_np, self._vq_codec.codebook, metric=self.metric.name.lower()\n            )\n            dists, cells = top_k(dists, k=self.n_probe)\n        else:\n            cells = np.zeros((n_data, 1), dtype=np.int64)\n\n        # if self.use_smart_probing and self.n_probe > 1:\n        #     p = -topk_sims.abs().sqrt()\n        #     p = torch.softmax(p / self.smart_probing_temperature, dim=-1)\n        #\n        #     # p_norm = p.norm(dim=-1)\n        #     # sqrt_d = self.n_probe ** 0.5\n        #     # score = 1 - (p_norm * sqrt_d - 1) / (sqrt_d - 1) - 1e-6\n        #     # n_probe_list = torch.ceil(score * (self.n_probe) ).long()\n        #\n        #     max_n_probe = torch.tensor(self.n_probe, device=self.device)\n        #     normalized_entropy = - torch.sum(p * torch.log2(p) / torch.log2(max_n_probe), dim=-1)\n        #     n_probe_list = torch.ceil(normalized_entropy * max_n_probe).long()\n        # else:\n        #     n_probe_list = None\n        return cells\n\n    def search_numpy(\n        self,\n        query_np: 'np.ndarray',\n        filter: Dict = {},\n        limit: int = 10,\n        **kwargs,\n    ):\n        \"\"\"Search the index and return distances to the query and ids of the closest documents.\n\n        :param query_np: matrix containing query vectors as rows\n        :param filter: the filtering conditions\n        :param limit: the number of results to get for each query document in search\n        \"\"\"\n\n        if not self.is_trained:\n            raise RuntimeError(f'The indexer is not trained, cannot add new documents')\n\n        dists, doc_ids = self._search_numpy(query_np, filter, limit)\n        return dists, doc_ids\n\n    def _search_numpy(self, query_np: 'np.ndarray', filter: Dict = {}, limit: int = 10):\n        \"\"\"Search approximate nearest vectors in different cells, returns distances and ids\n\n        :param query_np: matrix containing query vectors as rows\n        :param filter: the filtering conditions\n        :param limit: the number of results to get for each query document in search\n        \"\"\"\n        cells = self._cell_selection(query_np, limit)\n        where_clause, where_params = Filter(filter).parse_where_clause()\n\n        dists, ids = self._search_cells(\n            query=query_np,\n            cells=cells,\n            where_clause=where_clause,\n            where_params=where_params,\n            limit=limit,\n        )\n        return dists, ids\n\n    def delete(\n        self,\n        docs: Union['DocumentArray', List[str]],\n        raise_errors_on_not_found: bool = False,\n    ):\n        \"\"\"Delete entries from the index by id\n\n        :param raise_errors_on_not_found: whether to raise exception when id not found.\n        :param docs: the documents to delete\n        \"\"\"\n\n        super().delete(\n            docs if isinstance(docs, list) else docs[:, 'id'], raise_errors_on_not_found\n        )\n\n    def clear(self):\n        \"\"\"Clear the whole database\"\"\"\n        for cell_id in range(self.n_cells):\n            self.vec_index(cell_id).reset()\n            self.cell_table(cell_id).clear()\n            self.doc_store(cell_id).clear()\n        self.meta_table.clear()\n\n    def close(self):\n        for cell_id in range(self.n_cells):\n            self.doc_store(cell_id).close()\n\n    def encode(self, x: 'np.ndarray'):\n        n_data, _ = self._sanity_check(x)\n\n        if self._projector_codec:\n            x = self._projector_codec.encode(x)\n\n        if self._vq_codec:\n            x = self._pq_codec.encode(x)\n\n        return x\n\n    def decode(self, x: 'np.ndarray'):\n        assert len(x.shape) == 2\n        assert x.shape[1] == self.n_subvectors\n\n        if self._pq_codec:\n            x = self._pq_codec.decode(x)\n\n        if self._projector_codec:\n            x = self._projector_codec.decode(x)\n\n        return x\n\n    @property\n    def params_hash(self):\n        model_metas = (\n            f'n_dim: {self.n_dim} '\n            f'metric: {self.metric} '\n            f'n_cells: {self.n_cells} '\n            f'n_components: {self.n_components} '\n            f'n_subvectors: {self.n_subvectors}'\n        )\n        return hashlib.md5(f'{model_metas}'.encode()).hexdigest()\n\n    @property\n    def model_path(self):\n        return self.data_path / f'parameters-{self.params_hash}'\n\n    @property\n    def _vq_codec_path(self):\n        return self.model_path / f'vq_codec.params'\n\n    @property\n    def _pq_codec_path(self):\n        return self.model_path / f'pq_codec.params'\n\n    @property\n    def _projector_codec_path(self):\n        return self.model_path / f'projector_codec.params'\n\n    @property\n    def index_hash(self):\n        latest_commit = self.meta_table.get_latest_commit()\n        date_time = latest_commit[-1] if latest_commit else None\n        if date_time:\n            if platform.system() == 'Windows':\n                return date_time.isoformat('#', 'hours')\n            return date_time.isoformat('#', 'seconds')\n        else:\n            import datetime\n\n            return (\n                datetime.datetime.utcnow().isoformat('#', 'hours')\n                if platform.system() == 'Windows'\n                else datetime.datetime.utcnow().isoformat('#', 'seconds')\n            )\n\n    @property\n    def index_path(self):\n        if self.index_hash:\n            return (\n                self.data_path\n                / f'snapshot-{self.params_hash}'\n                / f'{self.index_hash}-SNAPSHOT'\n            )\n        return None\n\n    @property\n    def snapshot_path(self):\n        paths = list(\n            (self.data_path / f'snapshot-{self.params_hash}').glob(f'*-SNAPSHOT')\n        )\n\n        if paths:\n            paths = sorted(paths, key=lambda x: x.name)\n            return paths[-1]\n        return None\n\n    @property\n    def remote_store_client(self):\n        try:\n            import hubble\n\n            os.environ['JINA_AUTH_TOKEN'] = self.token\n            client = hubble.Client(max_retries=None, jsonify=True)\n            client.get_user_info()\n            return client\n        except Exception as ex:\n            logger.error(f'Not login to hubble yet.')\n            raise ex\n\n    def backup(self, target_name: Optional[str] = None, token: Optional[str] = None):\n        # file lock will be released when backup to remote, this will\n        # release the file lock. And it's only needed in Windows\n        # since we need to release file lock before we can access rocksdb files.\n        if not target_name:\n            logger.info('dump to local ...')\n            self.dump()\n        else:\n            if token is None:\n                logger.error(f'back up to remote needs token')\n            logger.info(f'dump to remote: {target_name}')\n            self.close()\n            self._backup_index_to_remote(target_name, token)\n\n    def restore(self, source_name: Optional[str] = None, token: Optional[str] = None):\n        # file lock will be released when restore from remote\n        if not source_name:\n            if self.total_docs > 0:\n                logger.info(f'restore Annlite from local')\n                self._rebuild_index_from_local()\n        else:\n            if token is None:\n                logger.error(f'restore from remote needs token')\n            logger.info(f'restore Annlite from artifact: {source_name}')\n            self.close()\n            self._rebuild_index_from_remote(source_name, token)\n\n    def dump_model(self):\n        logger.info(f'Save the parameters to {self.model_path}')\n        self.model_path.mkdir(parents=True, exist_ok=True)\n        if self._projector_codec:\n            self._projector_codec.dump(self._projector_codec_path)\n        if self._vq_codec:\n            self._vq_codec.dump(self._vq_codec_path)\n        if self._pq_codec:\n            self._pq_codec.dump(self._pq_codec_path)\n\n    def dump_index(self):\n        import shutil\n\n        logger.info(f'Save the indexer to {self.index_path}')\n        try:\n            if Path.exists(self.index_path):\n                logger.info(\n                    f'Index path {self.index_path} already exists, will be '\n                    f'overwritten'\n                )\n                shutil.rmtree(self.index_path)\n            self.index_path.mkdir(parents=True)\n\n            for cell_id in range(self.n_cells):\n                self.vec_index(cell_id).dump(self.index_path / f'cell_{cell_id}.hnsw')\n                self.cell_table(cell_id).dump(self.index_path / f'cell_{cell_id}.db')\n            self.meta_table.dump(self.index_path / f'meta.db')\n        except Exception as ex:\n            logger.error(f'Failed to dump the indexer, {ex!r}')\n\n            if self.index_path:\n                shutil.rmtree(self.index_path)\n\n    def dump(self):\n        self.dump_model()\n        self.dump_index()\n\n    def _backup_index_to_remote(self, target_name: str, token: str):\n\n        self.dump()\n\n        from .hubble_tools import Uploader\n\n        self.token = token\n        client = self.remote_store_client\n        uploader = Uploader(size_limit=self.size_limit, client=client)\n\n        for cell_id in range(self.n_cells):\n            # upload database\n            uploader.upload_directory(\n                Path(self.data_path) / f'cell_{cell_id}',\n                target_name=target_name,\n                type='database',\n                cell_id=cell_id,\n            )\n\n            # upload hnsw file\n            uploader.upload_file(\n                Path(self.index_path) / f'cell_{cell_id}.hnsw',\n                target_name=target_name,\n                type='hnsw',\n                cell_id=cell_id,\n            )\n\n            # upload cell_table\n            uploader.upload_file(\n                Path(self.index_path) / f'cell_{cell_id}.db',\n                target_name=target_name,\n                type='cell_table',\n                cell_id=cell_id,\n            )\n\n        # upload meta table\n        uploader.upload_file(\n            Path(self.index_path) / 'meta.db',\n            target_name=target_name,\n            type='meta_table',\n            cell_id=0,\n        )\n\n        # upload training model\n        uploader.archive_and_upload(\n            target_name,\n            'model',\n            'model.zip',\n            'all',\n            self.model_path.parent,\n            str(self.model_path.name),\n        )\n\n    def _rebuild_index_from_local(self):\n        if self.snapshot_path:\n            logger.info(f'Load the indexer from snapshot {self.snapshot_path}')\n            for cell_id in range(self.n_cells):\n                self.vec_index(cell_id).load(\n                    self.snapshot_path / f'cell_{cell_id}.hnsw'\n                )\n                self.cell_table(cell_id).load(self.snapshot_path / f'cell_{cell_id}.db')\n            self.meta_table.load(self.snapshot_path / f'meta.db')\n        else:\n            logger.info(f'Rebuild the indexer from scratch')\n            for cell_id in range(self.n_cells):\n                cell_size = self.doc_store(cell_id).size\n\n                if cell_size == 0:\n                    continue  # skip empty cell\n\n                logger.debug(\n                    f'Rebuild the index of cell-{cell_id} ({cell_size} docs)...'\n                )\n                for docs in self.documents_generator(cell_id, batch_size=10240):\n                    x = to_numpy_array(docs.embeddings)\n\n                    assigned_cells = np.ones(len(docs), dtype=np.int64) * cell_id\n                    super().insert(x, assigned_cells, docs, only_index=True)\n                logger.debug(f'Rebuild the index of cell-{cell_id} done')\n        if self.model_path:\n            logger.info(f'Load the model from {self.model_path}')\n            self._reload_models()\n\n    def _rebuild_index_from_remote(self, source_name: str, token: str):\n        import shutil\n\n        from .hubble_tools import Merger\n\n        self.token = token\n        client = self.remote_store_client\n        art_list = client.list_artifacts(\n            filter={'metaData.name': source_name}, pageSize=100\n        )\n        if len(art_list['data']) == 0:\n            logger.info(f'The indexer `{source_name}` not found. ')\n        else:\n            logger.info(f'Load the indexer `{source_name}` from remote store')\n\n            restore_path = self.data_path / 'restore'\n            merger = Merger(restore_path=restore_path, client=client)\n\n            for cell_id in range(self.n_cells):\n                # download hnsw files and merge and load\n                logger.info(f'Load the hnsw `{source_name}` from remote store')\n\n                hnsw_ids = merger.get_artifact_ids(\n                    art_list, type='hnsw', cell_id=cell_id\n                )\n                merger.download(ids=hnsw_ids, download_folder=f'hnsw_{cell_id}')\n                if len(hnsw_ids) > 1:\n                    merger.merge_file(\n                        inputdir=restore_path / f'hnsw_{cell_id}',\n                        outputdir=restore_path / f'hnsw_{cell_id}',\n                        outputfilename=Path(f'cell_{cell_id}.hnsw'),\n                    )\n                self.vec_index(cell_id).load(\n                    restore_path / f'hnsw_{cell_id}' / f'cell_{cell_id}.hnsw'\n                )\n                shutil.rmtree(restore_path / f'hnsw_{cell_id}')\n\n                # download cell_table files and merge and load\n                logger.info(f'Load the cell_table `{source_name}` from remote store')\n\n                cell_table_ids = merger.get_artifact_ids(\n                    art_list, type='cell_table', cell_id=cell_id\n                )\n                merger.download(\n                    ids=cell_table_ids, download_folder=f'cell_table_{cell_id}'\n                )\n                if len(cell_table_ids) > 1:\n                    merger.merge_file(\n                        inputdir=restore_path / f'cell_table_{cell_id}',\n                        outputdir=restore_path / f'cell_table_{cell_id}',\n                        outputfilename=Path(f'cell_{cell_id}.db'),\n                    )\n\n                self.cell_table(cell_id).load(\n                    restore_path / f'cell_table_{cell_id}' / f'cell_{cell_id}.db'\n                )\n                shutil.rmtree(restore_path / f'cell_table_{cell_id}')\n\n                # download database files and rebuild\n                logger.info(f'Load the database `{source_name}` from remote store')\n\n                database_ids = merger.get_artifact_ids(\n                    art_list, type='database', cell_id=cell_id\n                )\n                merger.download(ids=database_ids, download_folder='database')\n                for zip_file in list((restore_path / 'database').iterdir()):\n                    # default has only one cell\n                    shutil.unpack_archive(zip_file, self.data_path / f'cell_{cell_id}')\n                    for f in list(\n                        (\n                            self.data_path\n                            / f'cell_{cell_id}'\n                            / zip_file.name.split('.zip')[0]\n                        ).iterdir()\n                    ):\n                        origin_database_path = (\n                            self.data_path / f'cell_{cell_id}' / f.name\n                        )\n                        if origin_database_path.exists():\n                            origin_database_path.unlink()\n                        f.rename(self.data_path / f'cell_{cell_id}' / f.name)\n                    shutil.rmtree(\n                        self.data_path\n                        / f'cell_{cell_id}'\n                        / zip_file.name.split('.zip')[0]\n                    )\n                    Path(zip_file).unlink()\n            self._rebuild_database()\n\n            # download meta_table files\n            logger.info(f'Load the meta_table `{source_name}` from remote store')\n\n            meta_table_ids = merger.get_artifact_ids(\n                art_list, type='meta_table', cell_id=0\n            )\n            merger.download(ids=meta_table_ids, download_folder='meta_table')\n\n            if len(meta_table_ids) > 1:\n                merger.merge_file(\n                    inputdir=restore_path / 'meta_table',\n                    outputdir=restore_path / 'meta_table',\n                    outputfilename=Path('meta.db'),\n                )\n            self._meta_table.load(restore_path / 'meta_table' / 'meta.db')\n            shutil.rmtree(restore_path / 'meta_table')\n\n            # download model files\n            logger.info(f'Load the model `{source_name}` from remote store')\n            file_name = str(self.model_path.parent / f'{source_name}_model.zip')\n            model_id = [\n                art['_id']\n                for art in art_list['data']\n                if 'model' in art['metaData']['type']\n            ]\n            assert len(model_id) == 1\n            client.download_artifact(\n                id=model_id[0],\n                f=file_name,\n                show_progress=True,\n            )\n            shutil.unpack_archive(file_name, self.model_path.parent)\n            self._reload_models()\n            Path(file_name).unlink()\n\n            shutil.rmtree(restore_path)\n\n    @property\n    def is_trained(self):\n        if self._projector_codec and (not self._projector_codec.is_trained):\n            return False\n        if self._vq_codec and (not self._vq_codec.is_trained):\n            return False\n        if self._pq_codec and (not self._pq_codec.is_trained):\n            return False\n        return True\n\n    def _reload_models(self):\n        if self._projector_codec_path.exists():\n            self._projector_codec = ProjectorCodec.load(self._projector_codec_path)\n        if self._vq_codec_path.exists():\n            self._vq_codec = VQCodec.load(self._vq_codec_path)\n        if self._pq_codec_path.exists():\n            self._pq_codec = PQCodec.load(self._pq_codec_path)\n\n    @property\n    def use_smart_probing(self):\n        return self._use_smart_probing\n\n    @use_smart_probing.setter\n    def use_smart_probing(self, value):\n        assert type(value) is bool\n        self._use_smart_probing = value\n\n    @property\n    def stat(self):\n        \"\"\"Get information on status of the indexer.\"\"\"\n        return {\n            'total_docs': self.total_docs,\n            'index_size': self.index_size,\n            'n_cells': self.n_cells,\n            'n_dim': self.n_dim,\n            'n_components': self.n_components,\n            'metric': self.metric.name,\n            'is_trained': self.is_trained,\n        }\n\n    # @property\n    # def smart_probing_temperature(self):\n    #     return self._smart_probing_temperature\n    #\n    # @smart_probing_temperature.setter\n    # def smart_probing_temperature(self, value):\n    #     assert value > 0\n    #     assert self.use_smart_probing, 'set use_smart_probing to True first'\n    #     self._smart_probing_temperature = value\n"
  },
  {
    "path": "annlite/math.py",
    "content": "from typing import Tuple\n\nimport numpy as np\n\n\ndef l2_normalize(x: 'np.ndarray', eps: float = np.finfo(np.float32).eps):\n    \"\"\"Scale input vectors individually to unit norm.\n\n    :param x: The data to normalize\n    :param eps: a small jitter to avoid divde by zero\n    :return: Normalized input X\n    \"\"\"\n\n    norms = np.einsum('ij,ij->i', x, x)\n    np.sqrt(norms, norms)\n    constant_mask = norms < 10 * eps\n    norms[constant_mask] = 1.0\n    return x / norms[:, np.newaxis]\n\n\ndef cosine(\n    x_mat: 'np.ndarray', y_mat: 'np.ndarray', eps: float = np.finfo(np.float32).eps\n) -> 'np.ndarray':\n    \"\"\"Cosine distance between each row in x_mat and each row in y_mat.\n\n    :param x_mat: np.ndarray with ndim=2\n    :param y_mat: np.ndarray with ndim=2\n    :param eps: a small jitter to avoid divde by zero\n    :return: np.ndarray  with ndim=2\n    \"\"\"\n    return 1 - np.clip(\n        (np.dot(x_mat, y_mat.T) + eps)\n        / (\n            np.outer(np.linalg.norm(x_mat, axis=1), np.linalg.norm(y_mat, axis=1)) + eps\n        ),\n        -1,\n        1,\n    )\n\n\ndef sqeuclidean(x_mat: 'np.ndarray', y_mat: 'np.ndarray') -> 'np.ndarray':\n    \"\"\"Squared Euclidean distance between each row in x_mat and each row in y_mat.\n    :param x_mat: np.ndarray with ndim=2\n    :param y_mat: np.ndarray with ndim=2\n    :return: np.ndarray with ndim=2\n    \"\"\"\n    return (\n        np.sum(y_mat**2, axis=1)\n        + np.sum(x_mat**2, axis=1)[:, np.newaxis]\n        - 2 * np.dot(x_mat, y_mat.T)\n    )\n\n\ndef euclidean(x_mat: 'np.ndarray', y_mat: 'np.ndarray') -> 'np.ndarray':\n    \"\"\"Euclidean distance between each row in x_mat and each row in y_mat.\n\n    :param x_mat:  scipy.sparse like array with ndim=2\n    :param y_mat:  scipy.sparse like array with ndim=2\n    :return: np.ndarray  with ndim=2\n    \"\"\"\n    return np.sqrt(sqeuclidean(x_mat, y_mat))\n\n\ndef pdist(\n    x_mat: 'np.ndarray',\n    metric: str,\n) -> 'np.ndarray':\n    \"\"\"Computes Pairwise distances between observations in n-dimensional space.\n\n    :param x_mat: Union['np.ndarray','scipy.sparse.csr_matrix', 'scipy.sparse.coo_matrix'] of ndim 2\n    :param metric: string describing the metric type\n    :return: np.ndarray of ndim 2\n    \"\"\"\n    return cdist(x_mat, x_mat, metric)\n\n\ndef cdist(x_mat: 'np.ndarray', y_mat: 'np.ndarray', metric: str) -> 'np.ndarray':\n    \"\"\"Computes the pairwise distance between each row of X and each row on Y according to `metric`.\n    - Let `n_x = x_mat.shape[0]`\n    - Let `n_y = y_mat.shape[0]`\n    - Returns a matrix `dist` of shape `(n_x, n_y)` with `dist[i,j] = metric(x_mat[i], y_mat[j])`.\n    :param x_mat: numpy or scipy array of ndim 2\n    :param y_mat: numpy or scipy array of ndim 2\n    :param metric: string describing the metric type\n    :return: np.ndarray of ndim 2\n    \"\"\"\n    dists = {'cosine': cosine, 'sqeuclidean': sqeuclidean, 'euclidean': euclidean}[\n        metric\n    ](x_mat, y_mat)\n\n    return dists\n\n\ndef top_k(\n    values: 'np.ndarray', k: int, descending: bool = False\n) -> Tuple['np.ndarray', 'np.ndarray']:\n    \"\"\"Finds values and indices of the k largest entries for the last dimension.\n\n    :param values: array of distances\n    :param k: number of values to retrieve\n    :param descending: find top k biggest values\n    :return: indices and distances\n    \"\"\"\n    if descending:\n        values = -values\n\n    if k >= values.shape[1]:\n        idx = values.argsort(axis=1)[:, :k]\n        values = np.take_along_axis(values, idx, axis=1)\n    else:\n        idx_ps = values.argpartition(kth=k, axis=1)[:, :k]\n        values = np.take_along_axis(values, idx_ps, axis=1)\n        idx_fs = values.argsort(axis=1)\n        idx = np.take_along_axis(idx_ps, idx_fs, axis=1)\n        values = np.take_along_axis(values, idx_fs, axis=1)\n\n    if descending:\n        values = -values\n\n    return values, idx\n"
  },
  {
    "path": "annlite/profile.py",
    "content": "import cProfile\nimport pstats\nimport random\nfrom functools import wraps\n\nrandom.seed(20)\n\ntry:\n    import builtins\n\n    line_profile = builtins.profile\nexcept AttributeError:\n    # No line profiler, provide a pass-through version\n    def profile(func):\n        return func\n\n    line_profile = profile\n\n\ndef time_profile(\n    output_file=None, sort_by='cumulative', lines_to_print=None, strip_dirs=False\n):\n    \"\"\"A time profiler decorator.\n    Inspired by and modified the profile decorator of Giampaolo Rodola:\n    https://github.com/ekhoda/profile_decorator\n    Args:\n        output_file: str or None. Default is None\n            Path of the output file. If only name of the file is given, it's\n            saved in the current directory.\n            If it's None, the name of the decorated function is used.\n        sort_by: str or SortKey enum or tuple/list of str/SortKey enum\n            Sorting criteria for the Stats object.\n            For a list of valid string and SortKey refer to:\n            https://docs.python.org/3/library/profile.html#pstats.Stats.sort_stats\n        lines_to_print: int or None\n            Number of lines to print. Default (None) is for all the lines.\n            This is useful in reducing the size of the printout, especially\n            that sorting by 'cumulative', the time consuming operations\n            are printed toward the top of the file.\n        strip_dirs: bool\n            Whether to remove the leading path info from file names.\n            This is also useful in reducing the size of the printout\n    Returns:\n        Profile of the decorated function\n    \"\"\"\n\n    def inner(func):\n        @wraps(func)\n        def wrapper(*args, **kwargs):\n            _output_file = output_file or func.__name__ + '.prof'\n            pr = cProfile.Profile()\n            pr.enable()\n            retval = func(*args, **kwargs)\n            pr.disable()\n            pr.dump_stats(_output_file)\n\n            with open(_output_file, 'w') as f:\n                ps = pstats.Stats(pr, stream=f)\n                if strip_dirs:\n                    ps.strip_dirs()\n                if isinstance(sort_by, (tuple, list)):\n                    ps.sort_stats(*sort_by)\n                else:\n                    ps.sort_stats(sort_by)\n                ps.print_stats(lines_to_print)\n            return retval\n\n        return wrapper\n\n    return inner\n"
  },
  {
    "path": "annlite/storage/__init__.py",
    "content": ""
  },
  {
    "path": "annlite/storage/base.py",
    "content": "import abc\nfrom typing import TYPE_CHECKING, List, Optional\n\nif TYPE_CHECKING:\n    import numpy as np\n\nfrom ..enums import ExpandMode\n\n\nclass Storage(abc.ABC):\n    def __init__(\n        self,\n        initial_size: Optional[int] = None,\n        expand_step_size: int = 10240,\n        expand_mode: ExpandMode = ExpandMode.ADAPTIVE,\n    ):\n        if initial_size is None:\n            initial_size = expand_step_size\n        assert initial_size >= 0\n        assert expand_step_size > 0\n\n        self.initial_size = initial_size\n        self.expand_step_size = expand_step_size\n        self.expand_mode = expand_mode\n\n    @property\n    @abc.abstractmethod\n    def capacity(self) -> int:\n        ...\n\n    @property\n    @abc.abstractmethod\n    def size(self):\n        ...\n\n    @abc.abstractmethod\n    def clean(self):\n        ...\n\n    @abc.abstractmethod\n    def add(\n        self,\n        data: 'np.ndarray',\n        cells: 'np.ndarray',\n        ids: List[str],\n        doc_tags: Optional[List[dict]] = None,\n    ):\n        ...\n\n    @abc.abstractmethod\n    def delete(self, ids: List[str]):\n        ...\n\n    @abc.abstractmethod\n    def update(\n        self,\n        data: 'np.ndarray',\n        cells: 'np.ndarray',\n        ids: List[str],\n        doc_tags: Optional[List[dict]] = None,\n    ):\n        ...\n"
  },
  {
    "path": "annlite/storage/kv.py",
    "content": "import time\nimport warnings\nfrom pathlib import Path\nfrom typing import Dict, List, Union\n\nfrom docarray import Document, DocumentArray\nfrom rocksdict import Options, Rdict, ReadOptions, WriteBatch, WriteOptions\n\n\nclass DocStorage:\n    \"\"\"The backend storage engine of Documents\"\"\"\n\n    def __init__(\n        self,\n        path: Union[str, Path],\n        serialize_config: Dict = {},\n        create_if_missing: bool = True,\n        **kwargs,\n    ):\n        self._path = str(path)\n        self._serialize_config = serialize_config\n\n        self._kwargs = kwargs\n\n        self._init_db(create_if_missing=create_if_missing, **self._kwargs)\n\n    def _init_db(self, create_if_missing: bool = True, **kwargs):\n        opt = Options(raw_mode=True)\n\n        opt.optimize_for_point_lookup(1024)\n\n        opt.set_inplace_update_support(True)\n        opt.set_allow_concurrent_memtable_write(False)\n\n        # configure mem-table to a large value (256 MB)\n        opt.set_write_buffer_size(0x10000000)\n\n        # 256 MB file size\n        opt.set_target_file_size_base(0x10000000)\n\n        # # set to plain-table for better performance\n        # opt.set_plain_table_factory(PlainTableFactoryOptions())\n\n        opt.create_if_missing(create_if_missing)\n\n        self._db = Rdict(path=self._path, options=opt)\n\n        # get the size of the database, if it is not created, set it to 0\n        self._size = len(list(self._db.keys()))\n\n        self._is_closed = False\n\n    def insert(self, docs: 'DocumentArray'):\n        write_batch = WriteBatch(raw_mode=True)\n        write_opt = WriteOptions()\n        write_opt.sync = True\n        batch_size = 0\n        for doc in docs:\n            write_batch.put(doc.id.encode(), doc.to_bytes(**self._serialize_config))\n            batch_size += 1\n        self._db.write(write_batch, write_opt=write_opt)\n        self._size += batch_size\n\n    def update(self, docs: 'DocumentArray'):\n        write_batch = WriteBatch(raw_mode=True)\n        write_opt = WriteOptions()\n        write_opt.sync = True\n        for doc in docs:\n            key = doc.id.encode()\n            if key not in self._db:\n                raise ValueError(f'The Doc ({doc.id}) does not exist in database!')\n\n            write_batch.put(key, doc.to_bytes(**self._serialize_config))\n        self._db.write(write_batch, write_opt=write_opt)\n\n    def delete(self, doc_ids: List[str]):\n        write_batch = WriteBatch(raw_mode=True)\n        write_opt = WriteOptions()\n        write_opt.sync = True\n        for doc_id in doc_ids:\n            write_batch.delete(doc_id.encode())\n        self._db.write(write_batch, write_opt=write_opt)\n        self._size -= len(doc_ids)\n\n    def get(self, doc_ids: Union[str, list]) -> DocumentArray:\n        docs = DocumentArray()\n        if isinstance(doc_ids, str):\n            doc_ids = [doc_ids]\n\n        for doc_bytes in self._db[[k.encode() for k in doc_ids]]:\n            if doc_bytes:\n                docs.append(Document.from_bytes(doc_bytes, **self._serialize_config))\n\n        return docs\n\n    def clear(self):\n        if self._is_closed:\n            warnings.warn(\n                '`DocStorage` had been closed already, will skip this close operation.'\n            )\n        else:\n            self._db.close()\n        self._db.destroy(self._path)\n\n        # re-initialize the database for the next usage\n        self._init_db(create_if_missing=True, **self._kwargs)\n\n    def close(self):\n        if self._is_closed:\n            warnings.warn(\n                '`DocStorage` had been closed already, will skip this close operation.'\n            )\n            return\n        try:\n            self._db.flush(wait=True)\n            self._db.close()\n        except Exception as ex:\n            if 'No such file or directory' not in str(ex):\n                # this is a known bug, we can safely ignore it\n                raise ex\n        self._is_closed = True\n\n    def __len__(self):\n        return self._size\n\n    @property\n    def stat(self):\n        return {'entries': len(self)}\n\n    @property\n    def size(self):\n        return self.stat['entries']\n\n    @property\n    def last_transaction_id(self):\n        return self._db.latest_sequence_number()\n\n    def batched_iterator(self, batch_size: int = 1, **kwargs) -> 'DocumentArray':\n        count = 0\n        docs = DocumentArray()\n\n        read_opt = ReadOptions()\n\n        for value in self._db.values(read_opt=read_opt):\n            doc = Document.from_bytes(value, **self._serialize_config)\n            docs.append(doc)\n            count += 1\n\n            if count == batch_size:\n                yield docs\n                count = 0\n                docs = DocumentArray()\n\n        if count > 0:\n            yield docs\n"
  },
  {
    "path": "annlite/storage/table.py",
    "content": "import datetime\nimport sqlite3\nimport threading\nfrom pathlib import Path\nfrom typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union\n\nimport numpy as np\n\nif TYPE_CHECKING:\n    from docarray import DocumentArray\n\nsqlite3.register_adapter(np.int64, lambda x: int(x))\nsqlite3.register_adapter(np.int32, lambda x: int(x))\n\nCOLUMN_TYPE_MAPPING = {\n    float: 'FLOAT',\n    int: 'INTEGER',\n    bool: 'INTEGER',\n    str: 'TEXT',\n    bytes.__class__: 'BLOB',\n    bytes: 'BLOB',\n    memoryview: 'BLOB',\n    # datetime.datetime: 'TEXT',\n    # datetime.date: 'TEXT',\n    # datetime.time: 'TEXT',\n    None.__class__: 'TEXT',\n    # SQLite explicit types\n    'TEXT': 'TEXT',\n    'INTEGER': 'INTEGER',\n    'FLOAT': 'FLOAT',\n    'BLOB': 'BLOB',\n    'text': 'TEXT',\n    'integer': 'INTEGER',\n    'float': 'FLOAT',\n    'blob': 'BLOB',\n}\n\n# If numpy is available, add more types\nif np:\n    COLUMN_TYPE_MAPPING.update(\n        {\n            np.int8: 'INTEGER',\n            np.int16: 'INTEGER',\n            np.int32: 'INTEGER',\n            np.int64: 'INTEGER',\n            np.uint8: 'INTEGER',\n            np.uint16: 'INTEGER',\n            np.uint32: 'INTEGER',\n            np.uint64: 'INTEGER',\n            np.float16: 'FLOAT',\n            np.float32: 'FLOAT',\n            np.float64: 'FLOAT',\n        }\n    )\n\n\ndef _converting(value: Any) -> str:\n    if isinstance(value, bool):\n        if value:\n            return 1\n        else:\n            return 0\n\n    return str(value)\n\n\ndef time_now():\n    return datetime.datetime.utcnow()\n\n\ndef _get_table_names(\n    conn: 'sqlite3.Connection', fts4: bool = False, fts5: bool = False\n) -> List[str]:\n    \"\"\"A list of string table names in this database.\"\"\"\n    where = [\"type = 'table'\"]\n    if fts4:\n        where.append(\"sql like '%USING FTS4%'\")\n    if fts5:\n        where.append(\"sql like '%USING FTS5%'\")\n    sql = 'select name from sqlite_master where {}'.format(' AND '.join(where))\n    return [r[0] for r in conn.execute(sql).fetchall()]\n\n\nclass Table:\n    def __init__(\n        self,\n        name: str,\n        data_path: Optional[Union[Path, str]] = None,\n        detect_types: int = 0,\n        in_memory: bool = True,\n    ):\n        if in_memory:\n            self._conn_name = ':memory:'\n        else:\n            if isinstance(data_path, str):\n                data_path = Path(data_path)\n            self._conn_name = data_path / f'{name}.db'\n        self._name = name\n\n        self.detect_types = detect_types\n\n        self._conn = sqlite3.connect(\n            self._conn_name, detect_types=detect_types, check_same_thread=False\n        )\n        self._conn_lock = threading.Lock()\n\n    def execute(self, sql: str, commit: bool = True):\n        self._conn.execute(sql)\n        if commit:\n            self.commit()\n\n    def execute_many(self, sql: str, parameters: List[Tuple], commit: bool = True):\n        self._conn.executemany(sql, parameters)\n        if commit:\n            self.commit()\n\n    def commit(self):\n        self._conn.commit()\n\n    def create_table(self):\n        ...\n\n    def drop_table(self):\n        self._conn.execute(f'DROP table {self.name}')\n        self._conn.commit()\n\n    def clear(self):\n        \"\"\"Drop the table and create a new one\"\"\"\n        self.drop_table()\n        self.create_table()\n\n    def load(self, data_file: Union[str, Path]):\n        disk_db = sqlite3.connect(data_file, detect_types=self.detect_types)\n        disk_db.backup(self._conn)\n        disk_db.close()\n\n    def dump(self, data_file: Union[str, Path]):\n        backup_db = sqlite3.connect(data_file, detect_types=self.detect_types)\n        self._conn.backup(backup_db)\n        backup_db.close()\n\n    def close(self):\n        self._conn.close()\n\n    @property\n    def name(self):\n        return self._name\n\n    @property\n    def schema(self):\n        \"\"\"SQL schema for this database\"\"\"\n        result = []\n        for row in self._conn.execute(\n            f'''PRAGMA table_info(\"{self.name}\")'''\n        ).fetchall():\n            result.append(', '.join([str(_) for _ in row]))\n        return '\\n'.join(result)\n\n\nclass CellTable(Table):\n    def __init__(\n        self,\n        name: str,\n        columns: Optional[List[tuple]] = None,\n        in_memory: bool = True,\n        data_path: Optional[Path] = None,\n        lazy_create: bool = False,\n    ):\n        super().__init__(name, data_path=data_path, in_memory=in_memory)\n\n        self._columns = []\n        self._indexed_keys = set()\n\n        if columns is not None:\n            for name, dtype in columns:\n                self.add_column(name, dtype, True)\n        if not lazy_create:\n            self.create_table()\n\n    @property\n    def columns(self) -> List[str]:\n        return ['_id', '_doc_id'] + [c.split()[0] for c in self._columns]\n\n    def existed(self):\n        return self.name in _get_table_names(self._conn)\n\n    def add_column(self, name: str, dtype: str, create_index: bool = True):\n        self._columns.append(f'{name} {COLUMN_TYPE_MAPPING[dtype]}')\n        if create_index:\n            self._indexed_keys.add(name)\n\n    def create_index(self, column: str, commit: bool = True):\n        sql_statement = f'''CREATE INDEX idx_{column}_\n                            ON {self.name}({column})'''\n        self._conn.execute(sql_statement)\n\n        if commit:\n            self._conn.commit()\n\n    def create_table(self):\n        sql = f'''CREATE TABLE {self.name}\n                    (_id INTEGER PRIMARY KEY AUTOINCREMENT,\n                     _doc_id TEXT NOT NULL UNIQUE'''\n        if len(self._columns) > 0:\n            sql += ', ' + ', '.join(self._columns)\n        sql += ')'\n        self._conn.execute(sql)\n\n        for name in self._indexed_keys:\n            self.create_index(name, commit=False)\n        self._conn.commit()\n\n    def insert(\n        self,\n        docs: 'DocumentArray',\n        commit: bool = True,\n    ) -> List[int]:\n        \"\"\"Add a single record into the table.\n\n        :param docs: The list of dict docs\n        :param commit: If set, commit is applied\n        \"\"\"\n        sql_template = 'INSERT INTO {table}({columns}) VALUES ({placeholders});'\n\n        column_names = self.columns[1:]\n        columns = ', '.join(column_names)\n        placeholders = ', '.join('?' for c in column_names)\n        sql = sql_template.format(\n            table=self.name, columns=columns, placeholders=placeholders\n        )\n\n        values = []\n        docs_size = 0\n        for doc in docs:\n            doc_value = tuple(\n                [doc.id]\n                + [\n                    _converting(doc.tags[c]) if c in doc.tags else None\n                    for c in self.columns[2:]\n                ]\n            )\n            values.append(doc_value)\n            docs_size += 1\n\n        with self._conn_lock:\n            cursor = self._conn.cursor()\n            if docs_size > 1:\n                cursor.executemany(sql, values[:-1])\n\n            cursor.execute(sql, values[-1])\n            last_row_id = cursor.lastrowid\n            row_ids = list(range(last_row_id - len(docs), last_row_id))\n\n            if commit:\n                self._conn.commit()\n\n        return row_ids\n\n    def query(\n        self,\n        where_clause: str = '',\n        where_params: Tuple = (),\n        limit: int = -1,\n        offset: int = 0,\n        order_by: Optional[str] = None,\n        ascending: bool = True,\n    ) -> List[int]:\n        \"\"\"Query the records which matches the given conditions\n\n        :param where_clause: where clause for query\n        :param where_params: where parameters for query\n        :param limit: limit the number of results\n        :param offset: offset the number of results\n        :param order_by: order the results by the given column\n        :param ascending: order the results in ascending or descending order\n        :return: offsets list of matched docs\n        \"\"\"\n\n        where_conds = []\n        where = None\n        if where_clause:\n            where_conds.append(where_clause)\n            where = ' and '.join(where_conds)\n\n        _order_by = f'{order_by or \"_id\"} {\"ASC\" if ascending else \"DESC\"}'\n        _limit = f'LIMIT {limit}' if limit > 0 else ''\n        _offset = f'OFFSET {offset}' if offset > 0 else ''\n\n        if where:\n            sql = f'SELECT _id from {self.name} WHERE {where} ORDER BY {_order_by} {_limit} {_offset}'\n        else:\n            sql = f'SELECT _id from {self.name} ORDER BY {_order_by} {_limit} {_offset}'\n\n        params = tuple([_converting(p) for p in where_params])\n\n        # # EXPLAIN SQL query\n        # for row in self._conn.execute('EXPLAIN QUERY PLAN ' + sql, params):\n        #     print(row)\n\n        # Use `row_factor`\n        # https://docs.python.org/3.6/library/sqlite3.html#sqlite3.Connection.row_factory\n        def _offset_factory(_, record):\n            return record[0] - 1\n\n        self._conn.row_factory = _offset_factory\n\n        cursor = self._conn.cursor()\n\n        try:\n            if where:\n                offsets = cursor.execute(sql, params).fetchall()\n            else:\n                offsets = cursor.execute(sql).fetchall()\n            self._conn.row_factory = None\n            return offsets if offsets else []\n        except Exception as e:\n            self._conn.row_factory = None\n            raise e\n\n    def delete(self, doc_ids: List[str]):\n        \"\"\"Delete the docs\n\n        :param doc_ids: The IDs of docs\n        \"\"\"\n        sql = f'DELETE from {self.name} WHERE _doc_id = ?'\n        self._conn.executemany(sql, doc_ids)\n        self._conn.commit()\n\n    def get_docid_by_offset(self, offset: int):\n        sql = f'SELECT _doc_id from {self.name} WHERE _id = ? LIMIT 1;'\n        result = self._conn.execute(sql, (offset + 1,)).fetchone()\n        if result:\n            return result[0]\n        return None\n\n    def delete_by_offset(self, offset: int):\n        \"\"\"Delete the doc with specific offset\n\n        :param offset: The offset of the doc\n        \"\"\"\n        sql = f'DELETE FROM {self.name} WHERE _id = ?'\n        self._conn.execute(sql, (offset + 1,))\n        self._conn.commit()\n\n    def exist(self, doc_id: str):\n        sql = f'SELECT count(*) from {self.name} WHERE _doc_id = ?;'\n        return self._conn.execute(sql, (doc_id,)).fetchone()[0] > 0\n\n    def count(self, where_clause: str = '', where_params: Tuple = ()):\n        \"\"\"Return the total number of records which match with the given conditions.\n        :param where_clause: where clause for query\n        :param where_params: where parameters for query\n        :return: the total number of matched records\n        \"\"\"\n\n        if where_clause:\n            sql = 'SELECT count(_id) from {table} WHERE {where} LIMIT 1;'\n            where = where_clause\n            sql = sql.format(table=self.name, where=where)\n\n            params = tuple([_converting(p) for p in where_params])\n\n            # # EXPLAIN SQL query\n            # for row in self._conn.execute('EXPLAIN QUERY PLAN ' + sql, params):\n            #     print(row)\n            return self._conn.execute(sql, params).fetchone()[0]\n        else:\n            sql = f'SELECT count(_id) from {self.name};'\n            result = self._conn.execute(sql).fetchone()\n            if result[0]:\n                return result[0]\n            return 0\n\n    @property\n    def size(self):\n        return self.count()\n\n\nclass MetaTable(Table):\n    def __init__(\n        self,\n        name: str = 'meta',\n        data_path: Optional[Path] = None,\n        in_memory: bool = False,\n    ):\n        super().__init__(\n            name,\n            data_path=data_path,\n            detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES,\n            in_memory=in_memory,\n        )\n        self.create_table()\n\n    def create_table(self):\n        sql = f'''CREATE TABLE if not exists {self.name}\n                        (_doc_id TEXT NOT NULL PRIMARY KEY,\n                         cell_id INTEGER NOT NULL,\n                         offset INTEGER NOT NULL,\n                         time_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)'''\n\n        self._conn.execute(sql)\n\n        self._conn.execute(\n            f'CREATE INDEX if not exists idx_time_at_ ON {self.name}(time_at)'\n        )\n\n        self._conn.commit()\n\n    def iter_addresses(\n        self, time_since: 'datetime.datetime' = datetime.datetime(2020, 2, 2, 0, 0)\n    ):\n        sql = f'SELECT _doc_id, cell_id, offset from {self.name} WHERE time_at >= ? ORDER BY time_at ASC;'\n\n        cursor = self._conn.cursor()\n        for doc_id, cell_id, offset in cursor.execute(sql, (time_since,)):\n            yield doc_id, cell_id, offset\n\n    def get_latest_commit(self):\n        sql = f'SELECT _doc_id, cell_id, offset, time_at from {self.name} ORDER BY time_at DESC LIMIT 1;'\n\n        cursor = self._conn.execute(sql)\n        row = cursor.fetchone()\n        return row\n\n    def get_address(self, doc_id: str):\n        sql = f'SELECT cell_id, offset from {self.name} WHERE _doc_id = ? LIMIT 1;'\n        cursor = self._conn.execute(sql, (doc_id,))\n        row = cursor.fetchone()\n        return (row[0], row[1]) if row else (None, None)\n\n    def delete_address(self, doc_id: str, commit: bool = True):\n        sql = f'DELETE from {self.name} WHERE _doc_id = ?'\n        self._conn.execute(sql, (doc_id,))\n        if commit:\n            self._conn.commit()\n\n    def add_address(self, doc_id: str, cell_id: int, offset: int, commit: bool = True):\n        sql = f'INSERT OR REPLACE INTO {self.name}(_doc_id, cell_id, offset, time_at) VALUES (?, ?, ?, ?);'\n        self._conn.execute(\n            sql,\n            (doc_id, cell_id, offset, time_now()),\n        )\n        if commit:\n            self._conn.commit()\n\n    def bulk_add_address(\n        self,\n        doc_ids: List[str],\n        cell_ids: Union[List[int], np.ndarray],\n        offsets: Union[List[int], np.ndarray],\n        commit: bool = True,\n    ):\n        sql = f'INSERT OR REPLACE INTO {self.name}(_doc_id, cell_id, offset, time_at) VALUES (?, ?, ?, ?);'\n        self._conn.executemany(\n            sql,\n            [\n                (doc_id, cell_id, offset, time_now())\n                for doc_id, cell_id, offset in zip(doc_ids, cell_ids, offsets)\n            ],\n        )\n        if commit:\n            self._conn.commit()\n"
  },
  {
    "path": "annlite/utils.py",
    "content": "import os\nimport shutil\n\nimport numpy as np\nfrom docarray import Document, DocumentArray\n\n\ndef clean_workspace():\n    if os.path.exists('./data'):\n        shutil.rmtree('./data')\n\n    if os.path.exists('./workspace'):\n        shutil.rmtree('./workspace')\n\n\ndef docs_with_tags(N, D, probs, categories):\n\n    all_docs = []\n    start_current = 0\n    for k, prob in enumerate(probs):\n        n_current = int(N * prob)\n        X = np.random.random((n_current, D)).astype(np.float32)\n\n        docs = [\n            Document(\n                embedding=X[i],\n                id=f'{i+start_current}',\n                tags={\n                    'category': categories[k],\n                },\n            )\n            for i in range(n_current)\n        ]\n        all_docs.extend(docs)\n        start_current += n_current\n\n    return DocumentArray(all_docs)\n\n\ndef _precision(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of retrieved documents that are relevant to the query\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n\n    return n_predicted_and_relevant / len(predicted)\n\n\ndef _recall(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of the relevant documents that are successfully retrieved\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n    return n_predicted_and_relevant / len(relevant)\n\n\ndef evaluate(predicts, relevants, top_k):\n    recall = 0\n    precision = 0\n    for _predict, _relevant in zip(predicts, relevants):\n        _predict = np.array([int(x) for x in _predict])\n        recall += _recall(_predict, _relevant, top_k)\n        precision += _precision(_predict, _relevant, top_k)\n\n    return recall / len(predicts), precision / len(predicts)\n"
  },
  {
    "path": "benchmarks/filtering_bench.py",
    "content": "import os\nimport shutil\nimport tempfile\n\nimport numpy as np\nfrom jina import Document, DocumentArray\nfrom jina.logging.profile import TimeContext\n\nfrom annlite import AnnLite\n\nn_index = [10_000, 100_000, 500_000, 1_000_000]\n\nn_query = [1, 8, 64]\nD = 768\nR = 5\nB = 5000\nn_cells = 1\nprobs = [[0.20, 0.30, 0.50], [0.05, 0.15, 0.80]]\ncategories = ['comic', 'movie', 'audiobook']\n\n\ndef docs_with_tags(N, D, probs, categories):\n\n    all_docs = []\n    for k, prob in enumerate(probs):\n        n_current = int(N * prob)\n        X = np.random.random((n_current, D)).astype(np.float32)\n\n        docs = [\n            Document(\n                embedding=X[i],\n                tags={\n                    'category': categories[k],\n                },\n            )\n            for i in range(n_current)\n        ]\n        all_docs.extend(docs)\n\n    return DocumentArray(all_docs)\n\n\nresults = []\nfor n_i in n_index:\n\n    results_ni = []\n    for current_probs in probs:\n        with tempfile.TemporaryDirectory() as tmpdir:\n            columns = [('category', str)]\n            idxer = AnnLite(\n                D,\n                initial_size=n_i,\n                n_cells=n_cells,\n                columns=columns,\n                data_path=tmpdir,\n            )\n\n            da = docs_with_tags(n_i, D, current_probs, categories)\n\n            with TimeContext(f'indexing {n_i} docs') as t_i:\n                for i, _batch in enumerate(da.batch(batch_size=B)):\n                    idxer.index(_batch)\n\n            for cat, prob in zip(categories, current_probs):\n                f = {'category': {'$eq': cat}}\n\n                query_times = []\n                for n_q in n_query:\n                    qa = DocumentArray.empty(n_q)\n                    q_embs = np.random.random([n_q, D]).astype(np.float32)\n                    qa.embeddings = q_embs\n                    t_qs = []\n\n                    for _ in range(R):\n                        with TimeContext(f'searching {n_q} docs') as t_q:\n                            idxer.search(qa, filter=f)\n                        t_qs.append(t_q.duration)\n                    query_times.append(np.mean(t_qs[1:]))\n\n                print(f'\\n\\nprob={prob}, current_probs={current_probs}, n_i={n_i}\\n\\n')\n                results_ni.append([n_i, int(100 * prob), t_i.duration] + query_times)\n\n    results.append(results_ni)\n\n\ntitle = '| Stored data |% same filter| Indexing time | Query size=1  | Query size=8 | Query size=64|'\nprint(title)\nprint('|-----' * 6 + '|')\nfor block in results:\n    sorted_elements_in_block = np.argsort([b[1] for b in block])\n    for pos in sorted_elements_in_block:\n        res = block[pos]\n        print(\n            ''.join(\n                [f'| {x} ' for x in res[0:2]] + [f'| {x:.3f} ' for x in res[2:]] + ['|']\n            )\n        )\n"
  },
  {
    "path": "benchmarks/hnsw_bench.py",
    "content": "import tempfile\nimport time\nfrom datetime import date\n\nimport numpy as np\nimport pandas as pd\nfrom docarray import Document, DocumentArray\nfrom sklearn.datasets import make_blobs\nfrom sklearn.model_selection import train_test_split\n\nfrom annlite import AnnLite\nfrom annlite.math import cdist\nfrom annlite.math import top_k as _top_k\n\n\ndef _precision(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of retrieved documents that are relevant to the query\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n\n    return n_predicted_and_relevant / len(predicted)\n\n\ndef _recall(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of the relevant documents that are successfully retrieved\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n    return n_predicted_and_relevant / len(relevant)\n\n\ndef evaluate(predicts, relevants, eval_at):\n    recall = 0\n    precision = 0\n    for _predict, _relevant in zip(predicts, relevants):\n        _predict = np.array([int(x) for x in _predict])\n        recall += _recall(_predict, _relevant, top_k)\n        precision += _precision(_predict, _relevant, top_k)\n\n    return recall / len(predicts), precision / len(predicts)\n\n\n# N = 100_000 # number of data points\nNt = 125_000\nNq = 1\nD = 128  # dimentionality / number of features\ntop_k = 10\nn_cells = 64\nn_subvectors = 64\nn_queries = 1000\n\n# 2,000 128-dim vectors for training\nnp.random.seed(123)\nXtr, Xte = train_test_split(\n    make_blobs(n_samples=Nt, n_features=D)[0].astype(np.float32), test_size=20\n)\nprint(f'Xtr: {Xtr.shape} vs Xte: {Xte.shape}')\n\n\ndef get_documents(nr=10, index_start=0, embeddings=None):\n    for i in range(index_start, nr + index_start):\n        d = Document()\n        d.id = f'{i}'  # to test it supports non-int ids\n        d.embedding = embeddings[i - index_start]\n        yield d\n\n\nprecision_per_query = []\nrecall_per_query = []\nresults = []\n\nfor n_cells in [1, 8, 16, 32, 64, 128]:\n\n    with tempfile.TemporaryDirectory() as tmpdir:\n\n        pq = AnnLite(\n            D,\n            metric='euclidean',\n            n_cells=n_cells,\n            data_path=tmpdir,\n        )\n\n        t0 = time.time()\n        pq.train(Xtr[:20480])\n        train_time = abs(time.time() - t0)\n\n        t0 = time.time()\n        pq.index(DocumentArray(get_documents(len(Xtr), embeddings=Xtr)))\n        index_time = abs(t0 - time.time())\n\n        dists = cdist(Xte, Xtr, metric='euclidean')\n        true_dists, true_ids = _top_k(dists, top_k, descending=False)\n\n        t0 = time.time()\n        docs = DocumentArray(get_documents(len(Xte), embeddings=Xte))\n        pq.search(docs, limit=top_k)\n\n        query_time = abs(t0 - time.time())\n        pq_ids = []\n        for doc in docs:\n            pq_ids.append([m.id for m in doc.matches])\n\n        recall, precision = evaluate(pq_ids, true_ids, top_k)\n\n        results_dict = {\n            'precision': precision,\n            'recall': recall,\n            'train_time': train_time,\n            'index_time': index_time,\n            'query_time': query_time,\n            'query_qps': len(Xte) / query_time,\n            'index_qps': len(Xtr) / index_time,\n            'indexer_hyperparams': {'n_cells': n_cells},\n        }\n        print(results_dict)\n\n        results.append(results_dict)\n        pq.clear()\n        pq.close()\n\ntoday = date.today()\nresults_df = pd.DataFrame(results)\nresults_df.sort_values('recall', ascending=False)\nresults_df.to_csv(f'bench-results-{today.strftime(\"%b-%d-%Y\")}.csv')\n"
  },
  {
    "path": "bindings/hnsw_bindings.cpp",
    "content": "#include \"hnswlib.h\"\n#include <Python.h>\n#include <assert.h>\n#include <atomic>\n#include <iostream>\n#include <memory>\n#include <pybind11/numpy.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <thread>\n\nnamespace py = pybind11;\nusing namespace pybind11::literals; // needed to bring in _a literal\n\n/*\n * replacement for the openmp '#pragma omp parallel for' directive\n * only handles a subset of functionality (no reductions etc)\n * Process ids from start (inclusive) to end (EXCLUSIVE)\n *\n * The method is borrowed from nmslib\n */\ntemplate <class Function>\ninline void ParallelFor(size_t start, size_t end, size_t numThreads,\n                        Function fn) {\n  if (numThreads <= 0) {\n    numThreads = std::thread::hardware_concurrency();\n  }\n\n  if (numThreads == 1) {\n    for (size_t id = start; id < end; id++) {\n      fn(id, 0);\n    }\n  } else {\n    std::vector<std::thread> threads;\n    std::atomic<size_t> current(start);\n\n    // keep track of exceptions in threads\n    // https://stackoverflow.com/a/32428427/1713196\n    std::exception_ptr lastException = nullptr;\n    std::mutex lastExceptMutex;\n\n    for (size_t threadId = 0; threadId < numThreads; ++threadId) {\n      threads.push_back(std::thread([&, threadId] {\n        while (true) {\n          size_t id = current.fetch_add(1);\n\n          if ((id >= end)) {\n            break;\n          }\n\n          try {\n            fn(id, threadId);\n          } catch (...) {\n            std::unique_lock<std::mutex> lastExcepLock(lastExceptMutex);\n            lastException = std::current_exception();\n            /*\n             * This will work even when current is the largest value that\n             * size_t can fit, because fetch_add returns the previous value\n             * before the increment (what will result in overflow\n             * and produce 0 instead of current + 1).\n             */\n            current = end;\n            break;\n          }\n        }\n      }));\n    }\n    for (auto &thread : threads) {\n      thread.join();\n    }\n    if (lastException) {\n      std::rethrow_exception(lastException);\n    }\n  }\n}\n\ninline void assert_true(bool expr, const std::string &msg) {\n  if (expr == false)\n    throw std::runtime_error(\"Unpickle Error: \" + msg);\n  return;\n}\n\ntemplate <typename dist_t, typename data_t = float> class Index {\npublic:\n  Index(const std::string &space_name, const int dim)\n      : space_name(space_name), dim(dim) {\n    normalize = false;\n    if (space_name == \"l2\") {\n      l2space = new hnswlib::L2Space(dim);\n    } else if (space_name == \"ip\") {\n      l2space = new hnswlib::InnerProductSpace(dim);\n    } else if (space_name == \"cosine\") {\n      l2space = new hnswlib::InnerProductSpace(dim);\n      normalize = true;\n    } else {\n      throw new std::runtime_error(\n          \"Space name must be one of l2, ip, or cosine.\");\n    }\n    appr_alg = NULL;\n    ep_added = true;\n    index_inited = false;\n    num_threads_default = std::thread::hardware_concurrency();\n\n    default_ef = 10;\n    pq_enable = false;\n    pq_n_clusters = -1;\n    pq_n_subvectors = -1;\n    pq_d_subvector = -1;\n    pq_codec = py::none();\n  }\n\n  static const int ser_version = 1; // serialization version\n\n  std::string space_name;\n  int dim;\n  size_t seed;\n  size_t default_ef;\n\n  bool index_inited;\n  bool ep_added;\n  bool normalize;\n  size_t maxElements, M, efConstruction;\n  int num_threads_default;\n  hnswlib::labeltype cur_l;\n  hnswlib::HierarchicalNSW<dist_t> *appr_alg;\n  hnswlib::SpaceInterface<float> *l2space;\n\n  // quantization setting\n  bool pq_enable;\n  size_t pq_n_subvectors;\n  size_t pq_n_clusters;\n  size_t pq_d_subvector;\n  py::object pq_codec;\n\n  ~Index() {\n    delete l2space;\n    if (appr_alg)\n      delete appr_alg;\n    // WARNING: will python release the pq_codec?\n  }\n\n  void init_new_index(const size_t maxElements, const size_t M,\n                      const size_t efConstruction, const size_t random_seed,\n                      const py::object &pq_codec) {\n    if (appr_alg) {\n      throw new std::runtime_error(\"The index is already initiated.\");\n    }\n    if (!pq_codec.is_none()) {\n      _loadPQ(pq_codec);\n    }\n    this->cur_l = 0;\n    this->appr_alg = new hnswlib::HierarchicalNSW<dist_t>(\n        l2space, maxElements, M, efConstruction, random_seed);\n    this->index_inited = true;\n    this->ep_added = false;\n    this->appr_alg->ef_ = default_ef;\n    this->maxElements = maxElements;\n    this->M = M;\n    this->efConstruction = efConstruction;\n    this->seed = random_seed;\n  }\n\n  void loadPQ(const py::object &pq_codec) {\n    if (this->appr_alg) {\n      delete this->appr_alg;\n    }\n    if (pq_codec.is_none()) {\n      throw new std::runtime_error(\"Passed PQ class is none\");\n    }\n    _loadPQ(pq_codec);\n    this->cur_l = 0;\n    this->ep_added = false;\n    this->appr_alg = new hnswlib::HierarchicalNSW<dist_t>(\n        l2space, maxElements, M, efConstruction, seed);\n    this->appr_alg->ef_ = default_ef;\n  }\n\n  void set_ef(size_t ef) {\n    default_ef = ef;\n    if (appr_alg)\n      appr_alg->ef_ = ef;\n  }\n\n  void set_num_threads(int num_threads) {\n    this->num_threads_default = num_threads;\n  }\n\n  void saveIndex(const std::string &path_to_index) {\n    appr_alg->saveIndex(path_to_index);\n  }\n\n  void loadIndex(const std::string &path_to_index, size_t max_elements) {\n    if (appr_alg) {\n      std::cerr << \"Warning: Calling load_index for an already inited index. \"\n                   \"Old index is being deallocated.\";\n      delete appr_alg;\n    }\n    appr_alg = new hnswlib::HierarchicalNSW<dist_t>(l2space, path_to_index,\n                                                    false, max_elements);\n    cur_l = appr_alg->cur_element_count;\n    index_inited = true;\n  }\n\n  void normalize_vector(float *data, float *norm_array) {\n    float norm = 0.0f;\n    for (int i = 0; i < dim; i++)\n      norm += data[i] * data[i];\n    norm = 1.0f / (sqrtf(norm) + 1e-30f);\n    for (int i = 0; i < dim; i++)\n      norm_array[i] = data[i] * norm;\n  }\n\n  template <typename T>\n  void addRows_(int dim, int num_threads, const py::object &ids_,\n                const py::object &input, const py::object dtables) {\n\n    py::array_t<T, py::array::c_style | py::array::forcecast> items(input);\n    auto buffer = items.request();\n    size_t rows, features;\n\n    rows = buffer.shape[0];\n    features = buffer.shape[1];\n\n    if (!dtables.is_none()) {\n      hnswlib::pq_local_data_t pq_param;\n      py::array_t<float, py::array::c_style | py::array::forcecast>\n          dtable_items(dtables);\n      auto dtable_buffer = dtable_items.request();\n      pq_param.data = (float *)dtable_buffer.ptr;\n      pq_param.batch_len = rows;\n\n      this->l2space->attach_local_data(&pq_param);\n    }\n\n    if (features != dim)\n      throw std::runtime_error(\"wrong dimensionality of the vectors\");\n\n    if (num_threads <= 0)\n      num_threads = num_threads_default;\n    // avoid using threads when the number of searches is small:\n    if (rows <= num_threads * 4) {\n      num_threads = 1;\n    }\n\n    std::vector<size_t> ids;\n    if (!ids_.is_none()) {\n      py::array_t<size_t, py::array::c_style | py::array::forcecast> items(\n          ids_);\n      auto ids_numpy = items.request();\n      if (ids_numpy.ndim == 1 && ids_numpy.shape[0] == rows) {\n        std::vector<size_t> ids1(ids_numpy.shape[0]);\n        for (size_t i = 0; i < ids1.size(); i++) {\n          ids1[i] = items.data()[i];\n        }\n        ids.swap(ids1);\n      } else if (ids_numpy.ndim == 0 && rows == 1) {\n        ids.push_back(*items.data());\n      } else\n        throw std::runtime_error(\"wrong dimensionality of the labels\");\n    }\n\n    {\n\n      int start = 0;\n      if (!ep_added) {\n        size_t id = ids.size() ? ids.at(0) : (cur_l);\n        appr_alg->addPoint((void *)items.data(0), (size_t)id, 0);\n        start = 1;\n        ep_added = true;\n      }\n\n      py::gil_scoped_release l;\n      ParallelFor(start, rows, num_threads, [&](size_t row, size_t threadId) {\n        size_t id = ids.size() ? ids.at(row) : (cur_l + row);\n        appr_alg->addPoint((void *)items.data(row), (size_t)id, row);\n      });\n      cur_l += rows;\n    }\n\n    if (!dtables.is_none()) {\n      this->l2space->detach_local_data();\n    }\n  }\n  void addItems(const py::object &input, py::object ids_ = py::none(),\n                int num_threads = -1, py::object dtables = py::none()) {\n    // move dim check and normalization to python\n    if (!pq_enable) {\n      addRows_<float>(this->dim, num_threads, ids_, input, dtables);\n    } else {\n      if (pq_n_clusters <= (UINT8_MAX + 1)) {\n        addRows_<uint8_t>(pq_n_subvectors, num_threads, ids_, input, dtables);\n      } else if (pq_n_clusters <= (UINT16_MAX + 1)) {\n        addRows_<uint16_t>(pq_n_subvectors, num_threads, ids_, input, dtables);\n      } else if (pq_n_clusters <= (UINT32_MAX + 1)) {\n        addRows_<uint32_t>(pq_n_subvectors, num_threads, ids_, input, dtables);\n      }\n    }\n  }\n\n  template <typename T>\n  py::object knnQuery_return_numpy_(size_t k, int num_threads,\n                                    const py::object &input,\n                                    const py::object dtables) {\n    py::array_t<T, py::array::c_style | py::array::forcecast> items(input);\n    auto buffer = items.request();\n    hnswlib::labeltype *data_numpy_l;\n    dist_t *data_numpy_d;\n    size_t rows, features;\n    {\n      py::gil_scoped_release l;\n\n      rows = buffer.shape[0];\n      features = buffer.shape[1];\n\n      if (!dtables.is_none()) {\n        hnswlib::pq_local_data_t pq_param;\n        py::array_t<float, py::array::c_style | py::array::forcecast>\n            dtable_items(dtables);\n        auto dtable_buffer = dtable_items.request();\n        pq_param.data = (float *)dtable_buffer.ptr;\n        pq_param.batch_len = rows;\n\n        this->l2space->attach_local_data(&pq_param);\n      }\n\n      if (num_threads <= 0)\n        num_threads = num_threads_default;\n\n      // avoid using threads when the number of searches is small:\n      if (rows <= num_threads * 4) {\n        num_threads = 1;\n      }\n\n      data_numpy_l = new hnswlib::labeltype[rows * k];\n      data_numpy_d = new dist_t[rows * k];\n\n      ParallelFor(0, rows, num_threads, [&](size_t row, size_t threadId) {\n        std::priority_queue<std::pair<dist_t, hnswlib::labeltype>> result =\n            appr_alg->searchKnn((void *)items.data(row), k, row);\n        if (result.size() != k)\n          throw std::runtime_error(\n              \"Cannot return the results in a contigious 2D array. Probably \"\n              \"ef or M is too small\");\n        for (int i = k - 1; i >= 0; i--) {\n          auto &result_tuple = result.top();\n          data_numpy_d[row * k + i] = result_tuple.first;\n          data_numpy_l[row * k + i] = result_tuple.second;\n          result.pop();\n        }\n      });\n    }\n\n    if (!dtables.is_none()) {\n      this->l2space->detach_local_data();\n    }\n    py::capsule free_when_done_l(data_numpy_l, [](void *f) { delete[] f; });\n    py::capsule free_when_done_d(data_numpy_d, [](void *f) { delete[] f; });\n\n    return py::make_tuple(\n        py::array_t<hnswlib::labeltype>(\n            {rows, k}, // shape\n            {k * sizeof(hnswlib::labeltype),\n             sizeof(\n                 hnswlib::labeltype)}, // C-style contiguous strides for double\n            data_numpy_l,              // the data pointer\n            free_when_done_l),\n        py::array_t<dist_t>(\n            {rows, k}, // shape\n            {k * sizeof(dist_t),\n             sizeof(dist_t)}, // C-style contiguous strides for double\n            data_numpy_d,     // the data pointer\n            free_when_done_d));\n  }\n  py::object knnQuery_return_numpy(const py::object &input, size_t k = 1,\n                                   int num_threads = -1,\n                                   py::object dtables = py::none()) {\n    // move dim check and normalization to python\n    if (!pq_enable) {\n      return knnQuery_return_numpy_<float>(k, num_threads, input, dtables);\n    } else {\n      if (pq_n_clusters <= (UINT8_MAX + 1)) {\n        return knnQuery_return_numpy_<uint8_t>(k, num_threads, input, dtables);\n      } else if (pq_n_clusters <= (UINT16_MAX + 1)) {\n        return knnQuery_return_numpy_<uint16_t>(k, num_threads, input, dtables);\n      } else if (pq_n_clusters <= (UINT32_MAX + 1)) {\n        return knnQuery_return_numpy_<uint32_t>(k, num_threads, input, dtables);\n      }\n    }\n  }\n  template <typename T>\n  py::object knnQuery_with_filter_(size_t k, int num_threads,\n                                   const py::object &candidate_ids_,\n                                   const py::object &input,\n                                   const py::object dtables) {\n    py::array_t<T, py::array::c_style | py::array::forcecast> items(input);\n    auto buffer = items.request();\n    hnswlib::labeltype *data_numpy_l;\n    dist_t *data_numpy_d;\n\n    if (num_threads <= 0)\n      num_threads = num_threads_default;\n\n    size_t rows;\n    size_t features;\n\n    rows = buffer.shape[0];\n    features = buffer.shape[1];\n\n    if (!dtables.is_none()) {\n      hnswlib::pq_local_data_t pq_param;\n      py::array_t<float, py::array::c_style | py::array::forcecast>\n          dtable_items(dtables);\n      auto dtable_buffer = dtable_items.request();\n      pq_param.data = (float *)dtable_buffer.ptr;\n      pq_param.batch_len = rows;\n\n      this->l2space->attach_local_data(&pq_param);\n    }\n    // avoid using threads when the number of searches is small:\n\n    if (rows <= num_threads * 4) {\n      num_threads = 1;\n    }\n\n    // FuseFilter constructing\n    binary_fuse16_t filter(appr_alg->max_elements_);\n\n    if (!candidate_ids_.is_none()) {\n      py::array_t<size_t, py::array::c_style | py::array::forcecast> items(\n          candidate_ids_);\n      auto ids_numpy = items.request();\n\n      if (ids_numpy.ndim == 1) {\n        const size_t size = ids_numpy.shape[0];\n        std::vector<uint64_t> big_set;\n        big_set.reserve(size);\n        for (size_t i = 0; i < size; i++) {\n          big_set[i] = items.data()[i]; // we use contiguous values\n        }\n\n        if (!binary_fuse16_populate(big_set.data(), size, &filter)) {\n          throw std::runtime_error(\"failure to populate the fuse filter\");\n        }\n      } else\n        throw std::runtime_error(\"wrong dimensionality of the filter labels\");\n    }\n\n    {\n      py::gil_scoped_release l;\n\n      // would like to check the ownership of this data in more detail\n      data_numpy_l = new hnswlib::labeltype[rows * k];\n      data_numpy_d = new dist_t[rows * k];\n\n      ParallelFor(0, rows, num_threads, [&](size_t row, size_t threadId) {\n        std::priority_queue<std::pair<dist_t, hnswlib::labeltype>> result =\n            appr_alg->searchKnnWithFilter((void *)items.data(row), &filter, k,\n                                          row);\n        if (result.size() != k)\n          throw std::runtime_error(\n              \"Cannot return the results in a contigious 2D array. Probably \"\n              \"ef or M is too small\");\n        for (int i = k - 1; i >= 0; i--) {\n          auto &result_tuple = result.top();\n          data_numpy_d[row * k + i] = result_tuple.first;\n          data_numpy_l[row * k + i] = result_tuple.second;\n          result.pop();\n        }\n      });\n    }\n\n    if (!dtables.is_none()) {\n      this->l2space->detach_local_data();\n    }\n\n    py::capsule free_when_done_l(data_numpy_l, [](void *f) { delete[] f; });\n    py::capsule free_when_done_d(data_numpy_d, [](void *f) { delete[] f; });\n\n    return py::make_tuple(\n        py::array_t<hnswlib::labeltype>(\n            {rows, k}, // shape\n            {k * sizeof(hnswlib::labeltype),\n             sizeof(\n                 hnswlib::labeltype)}, // C-style contiguous strides for double\n            data_numpy_l,              // the data pointer\n            free_when_done_l),\n        py::array_t<dist_t>(\n            {rows, k}, // shape\n            {k * sizeof(dist_t),\n             sizeof(dist_t)}, // C-style contiguous strides for double\n            data_numpy_d,     // the data pointer\n            free_when_done_d));\n  }\n  py::object knnQuery_with_filter(py::object input,\n                                  py::object candidate_ids_ = py::none(),\n                                  size_t k = 1, int num_threads = -1,\n                                  py::object dtables = py::none()) {\n    // move dim check and normalization to python\n    if (!pq_enable) {\n      return knnQuery_with_filter_<float>(k, num_threads, candidate_ids_, input,\n                                          dtables);\n    } else {\n      if (pq_n_clusters <= (UINT8_MAX + 1)) {\n        return knnQuery_with_filter_<uint8_t>(k, num_threads, candidate_ids_,\n                                              input, dtables);\n      } else if (pq_n_clusters <= (UINT16_MAX + 1)) {\n        return knnQuery_with_filter_<uint16_t>(k, num_threads, candidate_ids_,\n                                               input, dtables);\n      } else if (pq_n_clusters <= (UINT32_MAX + 1)) {\n        return knnQuery_with_filter_<uint32_t>(k, num_threads, candidate_ids_,\n                                               input, dtables);\n      }\n    }\n  }\n\n  std::vector<std::vector<data_t>>\n  getDataReturnList(py::object ids_ = py::none()) {\n    std::vector<size_t> ids;\n    if (!ids_.is_none()) {\n      py::array_t<size_t, py::array::c_style | py::array::forcecast> items(\n          ids_);\n      auto ids_numpy = items.request();\n      std::vector<size_t> ids1(ids_numpy.shape[0]);\n      for (size_t i = 0; i < ids1.size(); i++) {\n        ids1[i] = items.data()[i];\n      }\n      ids.swap(ids1);\n    }\n\n    std::vector<std::vector<data_t>> data;\n    for (auto id : ids) {\n      data.push_back(appr_alg->template getDataByLabel<data_t>(id));\n    }\n    return data;\n  }\n\n  std::vector<hnswlib::labeltype> getIdsList() {\n\n    std::vector<hnswlib::labeltype> ids;\n\n    for (auto kv : appr_alg->label_lookup_) {\n      ids.push_back(kv.first);\n    }\n    return ids;\n  }\n\n  py::dict getAnnData() const { /* WARNING: Index::getAnnData is not thread-safe\n                                   with Index::addItems */\n\n    std::unique_lock<std::mutex> templock(appr_alg->global);\n\n    unsigned int level0_npy_size =\n        appr_alg->cur_element_count * appr_alg->size_data_per_element_;\n    unsigned int link_npy_size = 0;\n    std::vector<unsigned int> link_npy_offsets(appr_alg->cur_element_count);\n\n    for (size_t i = 0; i < appr_alg->cur_element_count; i++) {\n      unsigned int linkListSize =\n          appr_alg->element_levels_[i] > 0\n              ? appr_alg->size_links_per_element_ * appr_alg->element_levels_[i]\n              : 0;\n      link_npy_offsets[i] = link_npy_size;\n      if (linkListSize)\n        link_npy_size += linkListSize;\n    }\n\n    char *data_level0_npy = (char *)malloc(level0_npy_size);\n    char *link_list_npy = (char *)malloc(link_npy_size);\n    int *element_levels_npy =\n        (int *)malloc(appr_alg->element_levels_.size() * sizeof(int));\n\n    hnswlib::labeltype *label_lookup_key_npy = (hnswlib::labeltype *)malloc(\n        appr_alg->label_lookup_.size() * sizeof(hnswlib::labeltype));\n    hnswlib::tableint *label_lookup_val_npy = (hnswlib::tableint *)malloc(\n        appr_alg->label_lookup_.size() * sizeof(hnswlib::tableint));\n\n    memset(label_lookup_key_npy, -1,\n           appr_alg->label_lookup_.size() * sizeof(hnswlib::labeltype));\n    memset(label_lookup_val_npy, -1,\n           appr_alg->label_lookup_.size() * sizeof(hnswlib::tableint));\n\n    size_t idx = 0;\n    for (auto it = appr_alg->label_lookup_.begin();\n         it != appr_alg->label_lookup_.end(); ++it) {\n      label_lookup_key_npy[idx] = it->first;\n      label_lookup_val_npy[idx] = it->second;\n      idx++;\n    }\n\n    memset(link_list_npy, 0, link_npy_size);\n\n    memcpy(data_level0_npy, appr_alg->data_level0_memory_, level0_npy_size);\n    memcpy(element_levels_npy, appr_alg->element_levels_.data(),\n           appr_alg->element_levels_.size() * sizeof(int));\n\n    for (size_t i = 0; i < appr_alg->cur_element_count; i++) {\n      unsigned int linkListSize =\n          appr_alg->element_levels_[i] > 0\n              ? appr_alg->size_links_per_element_ * appr_alg->element_levels_[i]\n              : 0;\n      if (linkListSize) {\n        memcpy(link_list_npy + link_npy_offsets[i], appr_alg->linkLists_[i],\n               linkListSize);\n      }\n    }\n\n    py::capsule free_when_done_l0(data_level0_npy, [](void *f) { delete[] f; });\n    py::capsule free_when_done_lvl(element_levels_npy,\n                                   [](void *f) { delete[] f; });\n    py::capsule free_when_done_lb(label_lookup_key_npy,\n                                  [](void *f) { delete[] f; });\n    py::capsule free_when_done_id(label_lookup_val_npy,\n                                  [](void *f) { delete[] f; });\n    py::capsule free_when_done_ll(link_list_npy, [](void *f) { delete[] f; });\n\n    /*  TODO: serialize state of random generators appr_alg->level_generator_\n     * and appr_alg->update_probability_generator_  */\n    /*        for full reproducibility / to avoid re-initializing generators\n     * inside Index::createFromParams         */\n\n    return py::dict(\n        \"offset_level0\"_a = appr_alg->offsetLevel0_,\n        \"max_elements\"_a = appr_alg->max_elements_,\n        \"cur_element_count\"_a = appr_alg->cur_element_count,\n        \"size_data_per_element\"_a = appr_alg->size_data_per_element_,\n        \"label_offset\"_a = appr_alg->label_offset_,\n        \"offset_data\"_a = appr_alg->offsetData_,\n        \"max_level\"_a = appr_alg->maxlevel_,\n        \"enterpoint_node\"_a = appr_alg->enterpoint_node_,\n        \"max_M\"_a = appr_alg->maxM_, \"max_M0\"_a = appr_alg->maxM0_,\n        \"M\"_a = appr_alg->M_, \"mult\"_a = appr_alg->mult_,\n        \"ef_construction\"_a = appr_alg->ef_construction_,\n        \"ef\"_a = appr_alg->ef_, \"has_deletions\"_a = appr_alg->has_deletions_,\n        \"size_links_per_element\"_a = appr_alg->size_links_per_element_,\n\n        \"label_lookup_external\"_a = py::array_t<hnswlib::labeltype>(\n            {appr_alg->label_lookup_.size()}, // shape\n            {sizeof(\n                hnswlib::labeltype)}, // C-style contiguous strides for double\n            label_lookup_key_npy,     // the data pointer\n            free_when_done_lb),\n\n        \"label_lookup_internal\"_a = py::array_t<hnswlib::tableint>(\n            {appr_alg->label_lookup_.size()}, // shape\n            {sizeof(\n                hnswlib::tableint)}, // C-style contiguous strides for double\n            label_lookup_val_npy,    // the data pointer\n            free_when_done_id),\n\n        \"element_levels\"_a = py::array_t<int>(\n            {appr_alg->element_levels_.size()}, // shape\n            {sizeof(int)},      // C-style contiguous strides for double\n            element_levels_npy, // the data pointer\n            free_when_done_lvl),\n\n        // linkLists_,element_levels_,data_level0_memory_\n        \"data_level0\"_a = py::array_t<char>(\n            {level0_npy_size}, // shape\n            {sizeof(char)},    // C-style contiguous strides for double\n            data_level0_npy,   // the data pointer\n            free_when_done_l0),\n\n        \"link_lists\"_a = py::array_t<char>(\n            {link_npy_size}, // shape\n            {sizeof(char)},  // C-style contiguous strides for double\n            link_list_npy,   // the data pointer\n            free_when_done_ll)\n\n    );\n  }\n\n  py::dict getIndexParams() const { /* WARNING: Index::getAnnData is not\n                                       thread-safe with Index::addItems */\n    auto params = py::dict(\n        \"ser_version\"_a =\n            py::int_(Index<float>::ser_version), // serialization version\n        \"space\"_a = space_name, \"dim\"_a = dim, \"index_inited\"_a = index_inited,\n        \"ep_added\"_a = ep_added, \"normalize\"_a = normalize,\n        \"num_threads\"_a = num_threads_default, \"seed\"_a = seed);\n\n    if (index_inited == false)\n      return py::dict(**params, \"ef\"_a = default_ef);\n\n    auto ann_params = getAnnData();\n\n    return py::dict(**params, **ann_params);\n  }\n\n  static Index<float> *createFromParams(const py::dict d) {\n\n    // check serialization version\n    assert_true(((int)py::int_(Index<float>::ser_version)) >=\n                    d[\"ser_version\"].cast<int>(),\n                \"Invalid serialization version!\");\n\n    auto space_name_ = d[\"space\"].cast<std::string>();\n    auto dim_ = d[\"dim\"].cast<int>();\n    auto index_inited_ = d[\"index_inited\"].cast<bool>();\n\n    Index<float> *new_index = new Index<float>(space_name_, dim_);\n\n    /*  TODO: deserialize state of random generators into\n     * new_index->level_generator_ and new_index->update_probability_generator_\n     */\n    /*        for full reproducibility / state of generators is serialized\n     * inside Index::getIndexParams                      */\n    new_index->seed = d[\"seed\"].cast<size_t>();\n\n    if (index_inited_) {\n      new_index->appr_alg = new hnswlib::HierarchicalNSW<dist_t>(\n          new_index->l2space, d[\"max_elements\"].cast<size_t>(),\n          d[\"M\"].cast<size_t>(), d[\"ef_construction\"].cast<size_t>(),\n          new_index->seed);\n      new_index->cur_l = d[\"cur_element_count\"].cast<size_t>();\n    }\n\n    new_index->index_inited = index_inited_;\n    new_index->ep_added = d[\"ep_added\"].cast<bool>();\n    new_index->num_threads_default = d[\"num_threads\"].cast<int>();\n    new_index->default_ef = d[\"ef\"].cast<size_t>();\n\n    if (index_inited_)\n      new_index->setAnnData(d);\n\n    return new_index;\n  }\n\n  static Index<float> *createFromIndex(const Index<float> &index) {\n    return createFromParams(index.getIndexParams());\n  }\n\n  void setAnnData(const py::dict d) { /* WARNING: Index::setAnnData is not\n                                         thread-safe with Index::addItems */\n\n    std::unique_lock<std::mutex> templock(appr_alg->global);\n\n    assert_true(appr_alg->offsetLevel0_ == d[\"offset_level0\"].cast<size_t>(),\n                \"Invalid value of offsetLevel0_ \");\n    assert_true(appr_alg->max_elements_ == d[\"max_elements\"].cast<size_t>(),\n                \"Invalid value of max_elements_ \");\n\n    appr_alg->cur_element_count = d[\"cur_element_count\"].cast<size_t>();\n\n    assert_true(appr_alg->size_data_per_element_ ==\n                    d[\"size_data_per_element\"].cast<size_t>(),\n                \"Invalid value of size_data_per_element_ \");\n    assert_true(appr_alg->label_offset_ == d[\"label_offset\"].cast<size_t>(),\n                \"Invalid value of label_offset_ \");\n    assert_true(appr_alg->offsetData_ == d[\"offset_data\"].cast<size_t>(),\n                \"Invalid value of offsetData_ \");\n\n    appr_alg->maxlevel_ = d[\"max_level\"].cast<int>();\n    appr_alg->enterpoint_node_ = d[\"enterpoint_node\"].cast<hnswlib::tableint>();\n\n    assert_true(appr_alg->maxM_ == d[\"max_M\"].cast<size_t>(),\n                \"Invalid value of maxM_ \");\n    assert_true(appr_alg->maxM0_ == d[\"max_M0\"].cast<size_t>(),\n                \"Invalid value of maxM0_ \");\n    assert_true(appr_alg->M_ == d[\"M\"].cast<size_t>(), \"Invalid value of M_ \");\n    assert_true(appr_alg->mult_ == d[\"mult\"].cast<double>(),\n                \"Invalid value of mult_ \");\n    assert_true(appr_alg->ef_construction_ ==\n                    d[\"ef_construction\"].cast<size_t>(),\n                \"Invalid value of ef_construction_ \");\n\n    appr_alg->ef_ = d[\"ef\"].cast<size_t>();\n    appr_alg->has_deletions_ = d[\"has_deletions\"].cast<bool>();\n\n    assert_true(appr_alg->size_links_per_element_ ==\n                    d[\"size_links_per_element\"].cast<size_t>(),\n                \"Invalid value of size_links_per_element_ \");\n\n    auto label_lookup_key_npy =\n        d[\"label_lookup_external\"]\n            .cast<py::array_t<hnswlib::labeltype,\n                              py::array::c_style | py::array::forcecast>>();\n    auto label_lookup_val_npy =\n        d[\"label_lookup_internal\"]\n            .cast<py::array_t<hnswlib::tableint,\n                              py::array::c_style | py::array::forcecast>>();\n    auto element_levels_npy =\n        d[\"element_levels\"]\n            .cast<\n                py::array_t<int, py::array::c_style | py::array::forcecast>>();\n    auto data_level0_npy =\n        d[\"data_level0\"]\n            .cast<\n                py::array_t<char, py::array::c_style | py::array::forcecast>>();\n    auto link_list_npy =\n        d[\"link_lists\"]\n            .cast<\n                py::array_t<char, py::array::c_style | py::array::forcecast>>();\n\n    for (size_t i = 0; i < appr_alg->cur_element_count; i++) {\n      if (label_lookup_val_npy.data()[i] < 0) {\n        throw std::runtime_error(\"internal id cannot be negative!\");\n      } else {\n        appr_alg->label_lookup_.insert(std::make_pair(\n            label_lookup_key_npy.data()[i], label_lookup_val_npy.data()[i]));\n      }\n    }\n\n    memcpy(appr_alg->element_levels_.data(), element_levels_npy.data(),\n           element_levels_npy.nbytes());\n\n    unsigned int link_npy_size = 0;\n    std::vector<unsigned int> link_npy_offsets(appr_alg->cur_element_count);\n\n    for (size_t i = 0; i < appr_alg->cur_element_count; i++) {\n      unsigned int linkListSize =\n          appr_alg->element_levels_[i] > 0\n              ? appr_alg->size_links_per_element_ * appr_alg->element_levels_[i]\n              : 0;\n      link_npy_offsets[i] = link_npy_size;\n      if (linkListSize)\n        link_npy_size += linkListSize;\n    }\n\n    memcpy(appr_alg->data_level0_memory_, data_level0_npy.data(),\n           data_level0_npy.nbytes());\n\n    for (size_t i = 0; i < appr_alg->max_elements_; i++) {\n      unsigned int linkListSize =\n          appr_alg->element_levels_[i] > 0\n              ? appr_alg->size_links_per_element_ * appr_alg->element_levels_[i]\n              : 0;\n      if (linkListSize == 0) {\n        appr_alg->linkLists_[i] = nullptr;\n      } else {\n        appr_alg->linkLists_[i] = (char *)malloc(linkListSize);\n        if (appr_alg->linkLists_[i] == nullptr)\n          throw std::runtime_error(\n              \"Not enough memory: loadIndex failed to allocate linklist\");\n\n        memcpy(appr_alg->linkLists_[i],\n               link_list_npy.data() + link_npy_offsets[i], linkListSize);\n      }\n    }\n  }\n\n  void markDeleted(size_t label) { appr_alg->markDelete(label); }\n\n  void resizeIndex(size_t new_size) { appr_alg->resizeIndex(new_size); }\n\n  size_t getMaxElements() const { return appr_alg->max_elements_; }\n\n  size_t getCurrentCount() const { return appr_alg->cur_element_count; }\n\n  void _loadPQ(const py::object &pq_abstract) {\n    int exist_attr = 1, attr_correct = 1;\n    PyObject *pq_raw_ptr = pq_abstract.ptr();\n    exist_attr *= PyObject_HasAttrString(pq_raw_ptr, \"encode\");\n    exist_attr *= PyObject_HasAttrString(pq_raw_ptr, \"get_codebook\");\n    exist_attr *= PyObject_HasAttrString(pq_raw_ptr, \"get_subspace_splitting\");\n    if (exist_attr <= 0) {\n      throw py::index_error(\n          \"PQ class should at least have the following attributes:\\n\"\n          \"(encode, get_codebook, get_subspace_splitting)\");\n    }\n    attr_correct *=\n        PyMethod_Check(PyObject_GetAttrString(pq_raw_ptr, \"encode\"));\n    attr_correct *=\n        PyMethod_Check(PyObject_GetAttrString(pq_raw_ptr, \"get_codebook\"));\n    attr_correct *= PyMethod_Check(\n        PyObject_GetAttrString(pq_raw_ptr, \"get_subspace_splitting\"));\n    if (attr_correct <= 0) {\n      throw py::attribute_error(\n          \"PQ class have at least one of the following attributes' type \"\n          \"INCORRECT:\\n(encode: <bounded method>,\\n codebook: \"\n          \"<bounded method>,\\n get_subspace_splitting: <bounded method>)\");\n    }\n\n    this->pq_enable = true;\n    this->pq_codec = pq_abstract;\n\n    py::tuple subspaces_param = pq_abstract.attr(\"get_subspace_splitting\")();\n    this->pq_n_subvectors = py::cast<size_t>(subspaces_param[0]);\n    this->pq_n_clusters = py::cast<size_t>(subspaces_param[1]);\n    this->pq_d_subvector = py::cast<size_t>(subspaces_param[2]);\n\n    size_t pq_total_dims = (this->pq_n_subvectors * this->pq_d_subvector);\n    if (this->dim != pq_total_dims) {\n      throw py::value_error(\n          \"Initialization Error, expect HNSW.dim == \"\n          \"PQ.n_subvector*PQ.d_subvector, but got:\\n\"\n          \"HNSW.dim =\" +\n          std::to_string(this->dim) +\n          \", PQ.n_subvector*PQ.d_subvector=\" + std::to_string(pq_total_dims));\n    }\n    // reading codebook into float buffer\n    py::array_t<dist_t, py::array::c_style | py::array::forcecast> items(\n        pq_abstract.attr(\"get_codebook\")());\n    auto buffer = items.request();\n    if (buffer.ndim != 3 || buffer.shape[0] != pq_n_subvectors ||\n        buffer.shape[1] != pq_n_clusters || buffer.shape[2] != pq_d_subvector) {\n      py::print(\"Expect the codebook with shape (\", pq_n_subvectors,\n                pq_n_clusters, pq_d_subvector, \"seq\"_a = \",\");\n      py::print(\" but got shape \", buffer.shape);\n      throw py::attribute_error(\n          \"PQ class returning the codebook with wrong dimension\");\n    }\n    // std::shared_ptr<float> codebook_buffer((float *)buffer.ptr);\n    float *codebook_buffer = (float *)buffer.ptr;\n    if (l2space) {\n      delete l2space;\n    }\n    if (pq_n_clusters <= (UINT8_MAX + 1)) {\n      l2space = new hnswlib::PQ_Space<uint8_t>(space_name, pq_n_subvectors,\n                                               pq_n_clusters, pq_d_subvector,\n                                               codebook_buffer);\n    } else if (pq_n_clusters <= (UINT16_MAX + 1)) {\n      l2space = new hnswlib::PQ_Space<uint16_t>(space_name, pq_n_subvectors,\n                                                pq_n_clusters, pq_d_subvector,\n                                                codebook_buffer);\n    } else if (pq_n_clusters <= (UINT32_MAX + 1)) {\n      l2space = new hnswlib::PQ_Space<uint32_t>(space_name, pq_n_subvectors,\n                                                pq_n_clusters, pq_d_subvector,\n                                                codebook_buffer);\n    } else {\n      throw py::value_error(\n          \"PQ clustering exceed the maximum, annlite set the maximum of \"\n          \"clusters = \" +\n          std::to_string(UINT16_MAX + 1) +\n          \", but got PQ.n_clusters=\" + std::to_string(pq_n_clusters));\n    }\n  }\n};\n\nPYBIND11_PLUGIN(hnsw_bind) {\n  py::module m(\"hnsw_bind\");\n\n  py::class_<Index<float>>(m, \"Index\")\n      .def(py::init(&Index<float>::createFromParams), py::arg(\"params\"))\n      /* WARNING: Index::createFromIndex is not thread-safe with Index::addItems\n       */\n      .def(py::init(&Index<float>::createFromIndex), py::arg(\"index\"))\n      .def(py::init<const std::string &, const int>(), py::arg(\"space\"),\n           py::arg(\"dim\"))\n      .def(\"init_index\", &Index<float>::init_new_index, py::arg(\"max_elements\"),\n           py::arg(\"M\") = 16, py::arg(\"ef_construction\") = 200,\n           py::arg(\"random_seed\") = 100, py::arg(\"pq_codec\") = py::none())\n      .def(\"knn_query\", &Index<float>::knnQuery_return_numpy, py::arg(\"data\"),\n           py::arg(\"k\") = 1, py::arg(\"num_threads\") = -1,\n           py::arg(\"dtables\") = py::none())\n      .def(\"knn_query_with_filter\", &Index<float>::knnQuery_with_filter,\n           py::arg(\"data\"), py::arg(\"filters\") = py::none(), py::arg(\"k\") = 1,\n           py::arg(\"num_threads\") = -1, py::arg(\"dtables\") = py::none())\n      .def(\"add_items\", &Index<float>::addItems, py::arg(\"data\"),\n           py::arg(\"ids\") = py::none(), py::arg(\"num_threads\") = -1,\n           py::arg(\"dtables\") = py::none())\n      .def(\"get_items\", &Index<float, float>::getDataReturnList,\n           py::arg(\"ids\") = py::none())\n      .def(\"get_ids_list\", &Index<float>::getIdsList)\n      .def(\"set_ef\", &Index<float>::set_ef, py::arg(\"ef\"))\n      .def(\"set_num_threads\", &Index<float>::set_num_threads,\n           py::arg(\"num_threads\"))\n      .def(\"save_index\", &Index<float>::saveIndex, py::arg(\"path_to_index\"))\n      .def(\"load_index\", &Index<float>::loadIndex, py::arg(\"path_to_index\"),\n           py::arg(\"max_elements\") = 0)\n      .def(\"mark_deleted\", &Index<float>::markDeleted, py::arg(\"label\"))\n      .def(\"resize_index\", &Index<float>::resizeIndex, py::arg(\"new_size\"))\n      .def(\"get_max_elements\", &Index<float>::getMaxElements)\n      .def(\"get_current_count\", &Index<float>::getCurrentCount)\n      .def(\"loadPQ\", &Index<float>::loadPQ)\n      .def_readonly(\"space\", &Index<float>::space_name)\n      .def_readonly(\"dim\", &Index<float>::dim)\n      .def_readonly(\"pq_enable\", &Index<float>::pq_enable)\n      .def_readwrite(\"num_threads\", &Index<float>::num_threads_default)\n      .def_property(\n          \"ef\",\n          [](const Index<float> &index) {\n            return index.index_inited ? index.appr_alg->ef_ : index.default_ef;\n          },\n          [](Index<float> &index, const size_t ef_) {\n            index.default_ef = ef_;\n            if (index.appr_alg)\n              index.appr_alg->ef_ = ef_;\n          })\n      .def_property_readonly(\"max_elements\",\n                             [](const Index<float> &index) {\n                               return index.index_inited\n                                          ? index.appr_alg->max_elements_\n                                          : 0;\n                             })\n      .def_property_readonly(\"element_count\",\n                             [](const Index<float> &index) {\n                               return index.index_inited\n                                          ? index.appr_alg->cur_element_count\n                                          : 0;\n                             })\n      .def_property_readonly(\"ef_construction\",\n                             [](const Index<float> &index) {\n                               return index.index_inited\n                                          ? index.appr_alg->ef_construction_\n                                          : 0;\n                             })\n      .def_property_readonly(\"M\",\n                             [](const Index<float> &index) {\n                               return index.index_inited ? index.appr_alg->M_\n                                                         : 0;\n                             })\n\n      .def(py::pickle(\n          [](const Index<float> &ind) { // __getstate__\n            return py::make_tuple(\n                ind.getIndexParams()); /* Return dict (wrapped in a tuple) that\n                                          fully encodes state of the Index\n                                          object */\n          },\n          [](py::tuple t) { // __setstate__\n            if (t.size() != 1)\n              throw std::runtime_error(\"Invalid state!\");\n\n            return Index<float>::createFromParams(t[0].cast<py::dict>());\n          }))\n\n      .def(\"__repr__\", [](const Index<float> &a) {\n        return \"<pqlite.hnsw_bind.Index(space='\" + a.space_name +\n               \"', dim=\" + std::to_string(a.dim) + \")>\";\n      });\n\n  return m.ptr();\n}\n"
  },
  {
    "path": "bindings/pq_bindings.pyx",
    "content": "# distutils: language = c++\n\nimport numpy as np\n\ncimport cython\nfrom libc.stdint cimport (\n    int8_t,\n    int16_t,\n    int32_t,\n    int64_t,\n    uint8_t,\n    uint16_t,\n    uint32_t,\n    uint64_t,\n)\nfrom libcpp.vector cimport vector\n\nctypedef fused any_int:\n    uint8_t\n    uint16_t\n    uint32_t\n    uint64_t\n    int8_t\n    int16_t\n    int32_t\n    int64_t\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ncdef inline dist_pqcode_to_codebook(long M,const float[:,:] adtable, any_int[:] pq_code):\n    \"\"\"Compute the distance between each codevector and the pq_code of a query.\n\n    :param M: Number of sub-vectors in the original feature space.\n    :param adtable: 2D Memoryview[float] containing precomputed Asymmetric Distances.\n    :param pq_code: 1D Memoriview[any_int] containing a pq code.\n\n    :return: Distance between pq code and query according to the Asymmetric Distance table.\n\n    \"\"\"\n    cdef:\n        float dist = 0\n        int m\n\n    for m in range(M):\n        dist += adtable[m, pq_code[m]]\n\n    return dist\n\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ncpdef dist_pqcodes_to_codebooks(const float[:,:] adtable, any_int[:,:] pq_codes):\n    \"\"\"\n    Compute the distance between each row in pq_codes and each codevector using a adtable.\n\n    :param adtable: 2D Memoryview of precomputed Asymmetric Distances.\n    :param pq_codes: 2D Memoryview of pq_codes.\n\n    :return: List of Asymmetric Distances distances between pq_codes and the query.\n\n    This function is equivalent to:\n    '''\n        dists = np.zeros((N, )).astype(np.float32)\n        for n in range(N):\n            for m in range(M):\n                dists[n] += self.adtable[m][codes[n][m]]\n    '''\n\n    \"\"\"\n\n    cdef:\n        int m\n        int N = pq_codes.shape[0]\n        int M = pq_codes.shape[1]\n        vector[float] dists\n\n    for n in range(N):\n        dists.push_back(dist_pqcode_to_codebook(M, adtable, pq_codes[n,:]))\n\n    return dists\n\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ncpdef precompute_adc_table(const float[:] query,\n                                      long d_subvector,\n                                      long n_clusters,\n                                      const float[:,:,:] codebooks):\n    \"\"\"\n    Compute the  Asymmetric Distance Table between a query and a PQ space.\n\n    :param query: Memoryview a query in the original feature space (not a pqcode).\n    :param d_subvector: Number of dimensions in a subvector.\n    :param n_clusters: Number of clusters per sub-space (number of prototypes per sub-space).\n    :param codebooks: Memoryview containing the learned codevectors for each slice.\n        This is a 3D view with (slice index, prototype index, vector values).\n\n    :return: Memoryview with a 2D matrix containing the Asymmetric Distance Computation.\n\n    This function is equivalent to\n        '''\n        def numpy_adc_table(query, n_subvectors, n_clusters, d_subvector, codebooks):\n        adtable = np.empty((n_subvectors, n_clusters), dtype=np.float32)\n        for m in range(n_subvectors):\n            query_sub = query[m * d_subvector: (m + 1) * d_subvector]\n            adtable[m, :] = np.linalg.norm(codebooks[m] - query_sub, axis=1) ** 2\n\n        return adtable\n        '''\n    But avoids generating views and calling numpy functions.\n    \"\"\"\n\n    cdef:\n        int D = len(query)\n        int M = int(D/d_subvector)\n        int n_subvectors = int(D/d_subvector)\n        int m, i, k, ind_prototype, j\n        float[:, ::1] adtable = np.empty((M, n_clusters), dtype=np.float32)\n        float[:] query_subvec = np.empty(d_subvector, dtype=np.float32)\n        float[:] query_subcodeword = np.empty(d_subvector, dtype=np.float32)\n        float dist_subprototype_to_subquery, coord_j\n\n    for m in range(n_subvectors):\n\n        # load m'th subquery\n        i = 0\n        for k in range(m * d_subvector, (m + 1) * d_subvector):\n            query_subvec[i] = query[k]\n            i += 1\n\n        for ind_prototype in range(n_clusters):\n\n            # load prototype ind_prototype for the m'th subspace\n            for i in range(d_subvector):\n                query_subcodeword[i] = codebooks[m, ind_prototype, i]\n\n            # compute the distance between subprototype and subquery\n            dist_subprototype_to_subquery = 0.\n            for j in range(d_subvector):\n                coord_j = query_subcodeword[j] - query_subvec[j]\n                dist_subprototype_to_subquery += coord_j * coord_j\n\n            adtable[m, ind_prototype] = dist_subprototype_to_subquery\n\n    return adtable\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ncpdef batch_precompute_adc_table(const float[:, :] queries,\n                                      long d_subvector,\n                                      long n_clusters,\n                                      const float[:,:,:] codebooks):\n    \"\"\"\n    Compute the  Asymmetric Distance Table between a query and a PQ space.\n\n    :param query: Memoryview a query in the original feature space (not a pqcode).\n    :param d_subvector: Number of dimensions in a subvector.\n    :param n_clusters: Number of clusters per sub-space (number of prototypes per sub-space).\n    :param codebooks: Memoryview containing the learned codevectors for each slice.\n        This is a 3D view with (slice index, prototype index, vector values).\n\n    :return: Memoryview with a 2D matrix containing the Asymmetric Distance Computation.\n\n    This function is equivalent to\n        '''\n        def numpy_adc_table(query, n_subvectors, n_clusters, d_subvector, codebooks):\n        adtable = np.empty((n_subvectors, n_clusters), dtype=np.float32)\n        for m in range(n_subvectors):\n            query_sub = query[m * d_subvector: (m + 1) * d_subvector]\n            adtable[m, :] = np.linalg.norm(codebooks[m] - query_sub, axis=1) ** 2\n\n        return adtable\n        '''\n    But avoids generating views and calling numpy functions.\n    \"\"\"\n\n    cdef:\n        int N = queries.shape[0]\n        int D = queries.shape[1]\n        int n_subvectors = int(D/d_subvector)\n        int m, i, k, ind_prototype, j\n        float[:, :, :] adtable = np.empty((N, n_subvectors, n_clusters), dtype=np.float32)\n        float[:] query_subvec = np.empty(d_subvector, dtype=np.float32)\n        float[:] query_subcodeword = np.empty(d_subvector, dtype=np.float32)\n        float dist_subprototype_to_subquery, coord_j\n\n    for index in range(N):\n        for m in range(n_subvectors):\n\n            # load m'th subquery\n            i = 0\n            for k in range(m * d_subvector, (m + 1) * d_subvector):\n                query_subvec[i] = queries[index, k]\n                i += 1\n\n            for ind_prototype in range(n_clusters):\n\n                # load prototype ind_prototype for the m'th subspace\n                for i in range(d_subvector):\n                    query_subcodeword[i] = codebooks[m, ind_prototype, i]\n\n                # compute the distance between subprototype and subquery\n                dist_subprototype_to_subquery = 0.\n                for j in range(d_subvector):\n                    coord_j = query_subcodeword[j] - query_subvec[j]\n                    dist_subprototype_to_subquery += coord_j * coord_j\n\n                adtable[index, m, ind_prototype] = dist_subprototype_to_subquery\n\n    return adtable\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ncpdef batch_precompute_adc_table_ip(const float[:, :] queries,\n                                      long d_subvector,\n                                      long n_clusters,\n                                      const float[:,:,:] codebooks):\n    \"\"\"\n    Compute the  Asymmetric Distance Table between a query and a PQ space.\n\n    :param query: Memoryview a query in the original feature space (not a pqcode).\n    :param d_subvector: Number of dimensions in a subvector.\n    :param n_clusters: Number of clusters per sub-space (number of prototypes per sub-space).\n    :param codebooks: Memoryview containing the learned codevectors for each slice.\n        This is a 3D view with (slice index, prototype index, vector values).\n\n    :return: Memoryview with a 2D matrix containing the Asymmetric Distance Computation.\n\n    This function is equivalent to\n        '''\n        def numpy_adc_table(query, n_subvectors, n_clusters, d_subvector, codebooks):\n        adtable = np.empty((n_subvectors, n_clusters), dtype=np.float32)\n        for m in range(n_subvectors):\n            query_sub = query[m * d_subvector: (m + 1) * d_subvector]\n            adtable[m, :] = np.linalg.norm(codebooks[m] - query_sub, axis=1) ** 2\n\n        return adtable\n        '''\n    But avoids generating views and calling numpy functions.\n    \"\"\"\n\n    cdef:\n        int N = queries.shape[0]\n        int D = queries.shape[1]\n        int n_subvectors = int(D/d_subvector)\n        int m, i, k, ind_prototype, j\n        float[:, :, :] adtable = np.empty((N, n_subvectors, n_clusters), dtype=np.float32)\n        float[:] query_subvec = np.empty(d_subvector, dtype=np.float32)\n        float[:] query_subcodeword = np.empty(d_subvector, dtype=np.float32)\n        float dist_subprototype_to_subquery\n\n    for index in range(N):\n        for m in range(n_subvectors):\n\n            # load m'th subquery\n            i = 0\n            for k in range(m * d_subvector, (m + 1) * d_subvector):\n                query_subvec[i] = queries[index, k]\n                i += 1\n\n            for ind_prototype in range(n_clusters):\n\n                # load prototype ind_prototype for the m'th subspace\n                for i in range(d_subvector):\n                    query_subcodeword[i] = codebooks[m, ind_prototype, i]\n\n                # compute the distance between subprototype and subquery\n                dist_subprototype_to_subquery = 0.\n                for j in range(d_subvector):\n                    dist_subprototype_to_subquery += (query_subcodeword[j] * query_subvec[j])\n\n                adtable[index, m, ind_prototype] = dist_subprototype_to_subquery\n\n    return adtable\n"
  },
  {
    "path": "examples/annlite_vs_simpleindexer.py",
    "content": "import os\nimport shutil\nimport tempfile\nimport time\n\nimport numpy as np\nimport pandas as pd\nfrom jina import Document, DocumentArray, Flow\nfrom sklearn.datasets import make_blobs\nfrom sklearn.model_selection import train_test_split\n\nfrom executor.executor import AnnLiteIndexer\n\nNq = 1\nD = 128\ntop_k = 10\nR = 5\nn_cells = 64\nn_subvectors = 64\nn_queries = 1\n\nBENCHMARK_SIMPLEINDEXER = False\nBENCHMARK_ANNLITE = True\n\n\ndef _precision(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of retrieved documents that are relevant to the query\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n\n    return n_predicted_and_relevant / len(predicted)\n\n\ndef _recall(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of the relevant documents that are successfully retrieved\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n    return n_predicted_and_relevant / len(relevant)\n\n\ndef evaluate(predicts, relevants, eval_at):\n    recall = 0\n    precision = 0\n    for _predict, _relevant in zip(predicts, relevants):\n        _predict = np.array([int(x) for x in _predict])\n        recall += _recall(_predict, _relevant, top_k)\n        precision += _precision(_predict, _relevant, top_k)\n\n    return recall / len(predicts), precision / len(predicts)\n\n\ndef create_data(n_examples, D):\n    np.random.seed(123)\n    Xtr, Xte = train_test_split(\n        make_blobs(n_samples=n_examples, n_features=D)[0].astype(np.float32),\n        test_size=1,\n    )\n    return Xtr, Xte\n\n\ndef create_data_online(n_examples, D, batch_size):\n    np.random.seed(123)\n    num = 0\n    while True:\n        Xtr_batch = make_blobs(n_samples=batch_size, n_features=D)[0].astype(np.float32)\n        yield DocumentArray([Document(embedding=x) for x in Xtr_batch])\n        num += batch_size\n\n        if num + batch_size >= n_examples:\n            break\n\n    if num < n_examples:\n        Xtr_batch = make_blobs(n_samples=n_examples - num, n_features=D)[0].astype(\n            np.float32\n        )\n        yield DocumentArray([Document(embedding=x) for x in Xtr_batch])\n\n\ndef create_test_data(D, Nq):\n    np.random.seed(123)\n    Xte = make_blobs(n_samples=Nq, n_features=D)[0].astype(np.float32)\n    return DocumentArray([Document(embedding=x) for x in Xte])\n\n\nif BENCHMARK_SIMPLEINDEXER:\n\n    ################ SimpleIndexer Benchmark BEGIN #################\n    n_datasets = [10001, 50001, 200001, 400001]\n    times = []\n\n    for n_examples in n_datasets:\n        time_taken = 0\n\n        Xtr, Xte = create_data(n_examples, D)\n\n        with tempfile.TemporaryDirectory() as tmpdir:\n\n            f = Flow().add(\n                uses='jinahub://SimpleIndexer',\n                uses_with={'match_args': {'metric': 'euclidean', 'limit': 10}},\n                workspace=tmpdir,\n            )\n            docs = [Document(id=f'{i}', embedding=Xtr[i]) for i in range(len(Xtr))]\n\n            with f:\n                resp = f.post(\n                    on='/index',\n                    inputs=docs,\n                )\n\n            with f:\n                t0 = time.time()\n                resp = f.post(\n                    on='/search',\n                    inputs=DocumentArray([Document(embedding=Xte[0])]),\n                    return_results=True,\n                )\n                time_taken = time.time() - t0\n\n            times.append(time_taken)\n\n    df = pd.DataFrame({'n_examples': n_datasets, 'times': times})\n    df.to_csv('simpleindexer.csv')\n    print(df)\n    ################ SimpleIndexer Benchmark END #################\n\n\nif BENCHMARK_ANNLITE:\n\n    ################ AnnLite Benchmark BEGIN ######################\n    n_datasets = [10_000, 100_000, 500_000, 1_000_000, 10_000_000]\n    # n_datasets = [10_000, 100_000]\n    n_queries = [1, 8, 64]\n\n    batch_size = 4096\n    times = []\n\n    results = {}\n    for n_examples in n_datasets:\n        print(f'\\n\\nWorking with n_examples={n_examples}\\n\\n')\n        time_taken = 0\n\n        with tempfile.TemporaryDirectory() as tmpdir:\n\n            f = Flow().add(\n                uses=AnnLiteIndexer,\n                uses_with={\n                    'n_dim': D,\n                    'limit': 10,\n                },\n                workspace=tmpdir,\n            )\n\n            docs = create_data_online(n_examples, D, batch_size)\n\n            results_current = {}\n            with f:\n                time_taken = 0\n                for batch in docs:\n                    t0 = time.time()\n                    resp = f.post(on='/index', inputs=batch, request_size=10240)\n                    # This is done to avoid data creation time loaded in index time\n                    time_taken += time.time() - t0\n                results_current['index_time'] = time_taken\n\n            times_per_n_query = []\n            with f:\n                for n_query in n_queries:\n                    da_queries = create_test_data(D, n_query)\n                    t_qs = []\n                    for _ in range(R):\n                        t0 = time.time()\n                        resp = f.post(\n                            on='/search',\n                            inputs=da_queries,\n                            return_results=True,\n                        )\n                        time_taken = time.time() - t0\n                        t_qs.append(time_taken)\n                    # remove warm-up\n                    times_per_n_query.append(np.mean(t_qs[1:]))\n\n            results_current['query_times'] = times_per_n_query\n            print(f'==> query_times: {times_per_n_query}')\n            df = pd.DataFrame({'results': results_current})\n            df.to_csv(f'annlite_{n_examples}.csv')\n            results[n_examples] = results_current\n\n    df = pd.DataFrame(results)\n    df.to_csv('annlite.csv')\n    clean_workspace()\n    ################ AnnLite Benchmark END #########################\n"
  },
  {
    "path": "examples/filter_example.py",
    "content": "import os\nimport random\nimport shutil\n\nimport numpy as np\nfrom jina import Document, DocumentArray\nfrom jina.logging.profile import TimeContext\n\nfrom annlite import AnnLite\n\nn_index = [10_000, 100_000, 500_000, 1_000_000]\nn_index = [100_000]\nn_query = [1, 8, 64]\nn_query = [1]\nD = 768\nR = 5\nB = 5000\nn_cells = 1\n# probs =[[0.20, 0.30, 0.50],\n#         [0.05, 0.15, 0.80]]\ncategories = ['comic', 'movie', 'audiobook']\n\n\ndef clean_workspace():\n    if os.path.exists('./data'):\n        shutil.rmtree('./data')\n\n    if os.path.exists('./workspace'):\n        shutil.rmtree('./workspace')\n\n\ndef docs_with_tags(N, D, probs, categories):\n\n    all_docs = []\n    for k, prob in enumerate(probs):\n        n_current = int(N * prob)\n        X = np.random.random((n_current, D)).astype(np.float32)\n\n        docs = [\n            Document(\n                embedding=X[i],\n                tags={'category': categories[k], 'x': random.randint(0, 5)},\n            )\n            for i in range(n_current)\n        ]\n        all_docs.extend(docs)\n\n    return DocumentArray(all_docs)\n\n\nresults = []\nfor n_i in n_index:\n    clean_workspace()\n    results_ni = []\n    current_probs = [0.05, 0.15, 0.80]\n\n    columns = [('category', str)]\n    idxer = AnnLite(\n        D,\n        initial_size=n_i,\n        n_cells=n_cells,\n        data_path='./workspace',\n        columns=columns,\n    )\n\n    da = docs_with_tags(n_i, D, current_probs, categories)\n\n    with TimeContext(f'indexing {n_i} docs') as t_i:\n        for i, _batch in enumerate(da.batch(batch_size=B)):\n            idxer.index(_batch)\n\n    for cat, prob in zip(categories, current_probs):\n        f = {'category': {'$eq': cat}}\n\n        query_times = []\n        for n_q in n_query:\n            qa = DocumentArray.empty(n_q)\n            q_embs = np.random.random([n_q, D]).astype(np.float32)\n            qa.embeddings = q_embs\n            t_qs = []\n\n            for _ in range(R):\n                with TimeContext(f'searching {n_q} docs') as t_q:\n                    idxer.search(qa, filter=f)\n                t_qs.append(t_q.duration)\n            query_times.append(np.mean(t_qs[1:]))\n\n        print(f'\\n\\nprob={prob}, current_probs={current_probs}, n_i={n_i}\\n\\n')\n        results_ni.append([n_i, prob, t_i.duration] + query_times)\n\n    results.append(results_ni)\n\n\ntitle = '| Stored data |% same filter| Indexing time | Query size=1 | Query size=8 | Query size=64|'\nprint(title)\nprint('|-----' * 6 + '|')\nfor block in results:\n    sorted_elements_in_block = np.argsort([b[1] for b in block])\n    for pos in sorted_elements_in_block:\n        res = block[pos]\n        print(''.join([f'| {x:.3f} ' for x in res] + ['|']))\n"
  },
  {
    "path": "examples/hnsw_example.py",
    "content": "import random\nimport tempfile\n\nimport numpy as np\nfrom docarray import Document, DocumentArray\n\nfrom annlite import AnnLite\n\nN = 1000  # number of data points\nNq = 5\nNt = 2000\nD = 128  # dimensionality / number of features\n\ndirpath = tempfile.mkdtemp()\n\nwith tempfile.TemporaryDirectory() as tmpdirname:\n\n    index = AnnLite(\n        D, columns=[('x', float)], data_path=tmpdirname, include_metadata=True\n    )\n\n    X = np.random.random((N, D)).astype(\n        np.float32\n    )  # 10,000 128-dim vectors to be indexed\n\n    docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=X[i], tags={'x': random.random()})\n            for i in range(N)\n        ]\n    )\n    index.index(docs)\n\n    X = np.random.random((Nq, D)).astype(np.float32)  # a 128-dim query vector\n    query = DocumentArray([Document(embedding=X[i]) for i in range(5)])\n\n    index.search(query, filter={'x': {'$lt': 0.2}}, limit=10, include_metadata=True)\n\n    for m in query[0].matches:\n        print(f'{m.scores[\"euclidean\"].value} -> x={m.tags[\"x\"]}')\n        assert m.tags['x'] < 0.2\n\n    print(f'====')\n\n    index.search(query, filter={'x': {'$gte': 0.9}}, limit=10, include_metadata=True)\n\n    for m in query[0].matches:\n        print(f'{m.scores[\"euclidean\"].value} -> x={m.tags[\"x\"]}')\n        assert m.tags['x'] >= 0.9\n\n#\n# print(f'{[m.scores[\"euclidean\"].value for m in query[0].matches]}')\n# for i in range(len(query[0].matches) - 1):\n#     assert (\n#         query[0].matches[i].scores['euclidean'].value\n#         <= query[0].matches[i + 1].scores['euclidean'].value\n#     )\n"
  },
  {
    "path": "examples/pq_benchmark.py",
    "content": "import time\nfrom datetime import date\n\nimport numpy as np\nimport pandas as pd\nfrom docarray import Document, DocumentArray\nfrom sklearn.datasets import make_blobs\nfrom sklearn.model_selection import train_test_split\nfrom utils import evaluate\n\nfrom annlite import AnnLite\nfrom annlite.math import cdist\nfrom annlite.math import top_k as _top_k\n\n# N = 100_000 # number of data points\nNt = 100_020\nNq = 1\nD = 128  # dimentionality / number of features\ntop_k = 10\nn_cells = 64\nn_subvectors = 64\nn_queries = 1000\n\n# 2,000 128-dim vectors for training\nnp.random.seed(123)\nXtr, Xte = train_test_split(\n    make_blobs(n_samples=Nt, n_features=D)[0].astype(np.float32), test_size=20\n)\nprint(f'Xtr: {Xtr.shape} vs Xte: {Xte.shape}')\n\n\ndef get_documents(nr=10, index_start=0, embeddings=None):\n    for i in range(index_start, nr + index_start):\n        d = Document()\n        d.id = f'{i}'  # to test it supports non-int ids\n        d.embedding = embeddings[i - index_start]\n        yield d\n\n\nprecision_per_query = []\nrecall_per_query = []\nresults = []\n\nfor n_cells in [1, 4, 8]:\n    for n_subvectors in [64, 128]:\n\n        pq = AnnLite(D, metric='euclidean', n_cells=n_cells, n_subvectors=n_subvectors)\n\n        t0 = time.time()\n        pq.train(Xtr[:20480])\n        train_time = abs(time.time() - t0)\n\n        t0 = time.time()\n        pq.index(DocumentArray(get_documents(len(Xtr), embeddings=Xtr)))\n        index_time = abs(t0 - time.time())\n\n        dists = cdist(Xte, Xtr, metric='euclidean')\n        true_dists, true_ids = _top_k(dists, top_k, descending=False)\n\n        docs = DocumentArray(get_documents(len(Xte), embeddings=Xte))\n        t0 = time.time()\n        pq.search(docs, limit=top_k)\n        query_time = abs(t0 - time.time())\n\n        pq_ids = []\n        for doc in docs:\n            pq_ids.append([m.id for m in doc.matches])\n\n        recall, precision = evaluate(pq_ids, true_ids, top_k)\n\n        results_dict = {\n            'precision': precision,\n            'recall': recall,\n            'train_time': train_time,\n            'index_time': index_time,\n            'query_time': query_time,\n            'query_qps': len(Xte) / query_time,\n            'index_qps': len(Xtr) / index_time,\n            'indexer_hyperparams': {'n_cells': n_cells, 'n_subvectors': n_subvectors},\n        }\n        print(results_dict)\n\n        results.append(results_dict)\n        pq.clear()\n        pq.close()\n\ntoday = date.today()\nresults_df = pd.DataFrame(results)\nresults_df.sort_values('recall', ascending=False)\nresults_df.to_csv(f'bench-results-{today.strftime(\"%b-%d-%Y\")}.csv')\n"
  },
  {
    "path": "examples/pqlinearscann_benchmark_with_filtering.py",
    "content": "import numpy as np\nfrom docarray import Document, DocumentArray\nfrom docarray.math.distance import cdist\nfrom docarray.math.helper import top_k as _top_k\nfrom jina.logging.profile import TimeContext\nfrom utils import clean_workspace, docs_with_tags, evaluate\n\nfrom annlite import AnnLite\n\nn_index = [10_000, 100_000, 500_000, 1_000_000]\nn_query = [1, 8, 64]\nD = 768\nR = 5\nB = 100_000\nn_cells = 1\nprobs = [[0.20, 0.30, 0.50], [0.05, 0.15, 0.80]]\n\ncategories = ['comic', 'movie', 'audiobook']\n\ntop_k = 20\nn_cells = 1\nn_subvectors = D\n\nresults = []\nfor n_i in n_index:\n\n    results_ni = []\n    for current_probs in probs:\n\n        clean_workspace()\n        columns = [('category', str)]\n        indexer = AnnLite(\n            D,\n            initial_size=n_i,\n            n_subvectors=n_subvectors,\n            n_cells=n_cells,\n            metas={'workspace': './workspace'},\n            columns=columns,\n        )\n\n        da = docs_with_tags(n_i, D, current_probs, categories)\n        da_embeddings = da.embeddings\n\n        with TimeContext(f'indexing {n_i} docs') as t_i:\n            n_train_quantizer = min(n_i, 20_000)\n            row_ids = np.random.choice(range(n_i), n_train_quantizer, replace=False)\n            indexer.partial_train(da_embeddings[row_ids, :])\n\n            indexer.build_codebook()\n            for i, _batch in enumerate(da.batch(batch_size=B)):\n                indexer.index(_batch)\n\n        for cat, prob in zip(categories, current_probs):\n            f = {'category': {'$eq': cat}}\n\n            indices_cat = (\n                np.array([t['category'] for t in da.get_attributes('tags')]) == cat\n            )\n            ids_indices_cat = np.array(\n                [d.id for d in da if d.tags['category'] == cat], dtype='int'\n            )\n\n            da_embeddings_cat = da_embeddings[indices_cat, :]\n\n            query_times = []\n            for n_q in n_query:\n                qa = DocumentArray.empty(n_q)\n                q_embs = np.random.random([n_q, D]).astype(np.float32)\n                qa.embeddings = q_embs\n                t_qs = []\n\n                for _ in range(R):\n                    with TimeContext(f'searching {n_q} docs') as t_q:\n                        indexer.search(qa, filter=f, limit=top_k)\n                    t_qs.append(t_q.duration)\n\n                query_times.append(np.mean(t_qs[1:]))\n\n                if n_q == 8:\n                    dists = cdist(q_embs, da_embeddings_cat, metric='euclidean')\n                    true_dists, true_local_ids = _top_k(dists, top_k, descending=False)\n\n                    # Note:x\n                    #   `true_ids` are not really positions within the original data\n                    #   but positions within the subset `da_embeddings_cat`\n                    #   We need to go from these positions to the original positions\n                    true_ids = ids_indices_cat[true_local_ids]\n                    ids = []\n                    for doc in qa:\n                        ids.append([m.id for m in doc.matches])\n\n                    recall, precision = evaluate(ids, true_ids, top_k)\n\n            print(\n                f'\\n\\nprob={prob}, current_probs={current_probs}, n_i={n_i}, recall={recall}\\n\\n'\n            )\n            results_ni.append([n_i, prob, t_i.duration] + query_times + [recall])\n\n    results.append(results_ni)\n\n\ntitle = '| Stored data |% same filter| Indexing time | Query size=1 | Query size=8 | Query size=64| Recall |'\nprint(title)\nprint('|-----' * 6 + '|')\nfor block in results:\n    sorted_elements_in_block = np.argsort([b[1] for b in block])\n    for pos in sorted_elements_in_block:\n        res = block[pos]\n        print(''.join([f'| {x:.3f} ' for x in res] + ['|']))\n"
  },
  {
    "path": "examples/utils.py",
    "content": "import os\nimport shutil\n\nimport numpy as np\nfrom docarray import Document, DocumentArray\n\n\ndef clean_workspace():\n    if os.path.exists('./data'):\n        shutil.rmtree('./data')\n\n    if os.path.exists('./workspace'):\n        shutil.rmtree('./workspace')\n\n\ndef docs_with_tags(N, D, probs, categories):\n\n    all_docs = []\n    start_current = 0\n    for k, prob in enumerate(probs):\n        n_current = int(N * prob)\n        X = np.random.random((n_current, D)).astype(np.float32)\n\n        docs = [\n            Document(\n                embedding=X[i],\n                id=f'{i+start_current}',\n                tags={\n                    'category': categories[k],\n                },\n            )\n            for i in range(n_current)\n        ]\n        all_docs.extend(docs)\n        start_current += n_current\n\n    return DocumentArray(all_docs)\n\n\ndef _precision(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of retrieved documents that are relevant to the query\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n\n    return n_predicted_and_relevant / len(predicted)\n\n\ndef _recall(predicted, relevant, eval_at):\n    \"\"\"\n    fraction of the relevant documents that are successfully retrieved\n    \"\"\"\n    if eval_at == 0:\n        return 0.0\n    predicted_at_k = predicted[:eval_at]\n    n_predicted_and_relevant = len(set(predicted_at_k).intersection(set(relevant)))\n    return n_predicted_and_relevant / len(relevant)\n\n\ndef evaluate(predicts, relevants, top_k):\n    recall = 0\n    precision = 0\n    for _predict, _relevant in zip(predicts, relevants):\n        _predict = np.array([int(x) for x in _predict])\n        recall += _recall(_predict, _relevant, top_k)\n        precision += _precision(_predict, _relevant, top_k)\n\n    return recall / len(predicts), precision / len(predicts)\n"
  },
  {
    "path": "executor/Dockerfile",
    "content": "FROM jinaai/jina:3-py38-perf\n\nRUN apt-get update && apt-get install --no-install-recommends -y gcc g++ git \\\n    && rm -rf /var/lib/apt/lists/*\n\nCOPY . /workspace\nWORKDIR /workspace\n\nRUN pip install -r requirements.txt --no-cache-dir\n\nENTRYPOINT [\"jina\", \"executor\", \"--uses\", \"config.yml\"]\n"
  },
  {
    "path": "executor/README.md",
    "content": "# AnnLiteIndexer\n\n`AnnLiteIndexer` uses the [AnnLite](https://github.com/jina-ai/annlite) class for indexing Jina `Document` objects.\nThe `AnnLite` class partitions the data into cells at index time, and instantiates a \"sub-indexer\" in each cell.  Search is performed aggregating results retrieved from cells.\n\nThis indexer is recommended to be used when an application requires **search with filters** applied on `Document` tags.\nThe `filtering query language` is based on [MongoDB's query and projection operators](https://docs.mongodb.com/manual/reference/operator/query/). We currently support a subset of those selectors.\nThe tags filters can be combined with `$and` and `$or`:\n\n- `$eq` - Equal to (number, string)\n- `$ne` - Not equal to (number, string)\n- `$gt` - Greater than (number)\n- `$gte` - Greater than or equal to (number)\n- `$lt` - Less than (number)\n- `$lte` - Less than or equal to (number)\n- `$in` - Included in an array\n- `$nin` - Not included in an array\n\nFor example, we want to search for a product with a price no more than `50$`.\n```python\nindex.search(query, filter={\"price\": {\"$lte\": 50}})\n```\n\nMore example filter expresses\n\n- A Nike shoes with white color\n\n```JSON\n{\n  \"brand\": {\"$eq\": \"Nike\"},\n  \"category\": {\"$eq\": \"Shoes\"},\n  \"color\": {\"$eq\": \"White\"}\n}\n```\n\nOr\n\n```JSON\n{\n  \"$and\":\n    {\n      \"brand\": {\"$eq\": \"Nike\"},\n      \"category\": {\"$eq\": \"Shoes\"},\n      \"color\": {\"$eq\": \"White\"}\n    }\n}\n```\n\n\n- A Nike shoes or price less than `100$`\n\n```JSON\n{\n  \"$or\":\n    {\n      \"brand\": {\"$eq\": \"Nike\"},\n      \"price\": {\"$lt\": 100}\n    }\n}\n```\n\n## Performance\n\nOne can run `benchmark.py` to get a quick performance overview.\n\n|Stored data| Indexing time | Query size=1 | Query size=8 | Query size=64|\n|---|---|---|---|---|\n|10000 | 2.970 | 0.002 | 0.013 | 0.100|\n|100000 | 76.474 | 0.011 | 0.078 | 0.649|\n|500000 | 467.936 | 0.046 | 0.356 | 2.823|\n|1000000 | 1025.506 | 0.091 | 0.695 | 5.778|\n\n## Getting Started\n\nFor an in-depth overview of the features of AnnLite\nyou can follow along with one of the examples below:\n\n\n| Name                                         | Link  |\n|----------------------------------------------|---|\n| E-commerce product image search with AnnLite | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/jina-ai/pqlite/blob/main/notebooks/fashion_product_search.ipynb)|\n\n## Quick Start\n\n`AnnLiteIndexer` stores  `Document` objects at the  `workspace` directory, specified under the [`metas`](https://docs.jina.ai/fundamentals/executor/executor-built-in-features/#meta-attributes) attribute.\n\n#### Example: Selecting items whose 'price' is less than 50\n\nIf documents have a tag `'price'`  that stores floating point values this indexer allows searching documents with a filter, such as  `price <= 50`.\n\n```python\ncolumns = [('price', 'float')]\n\nf = Flow().add(\n    uses='jinahub://AnnLiteIndexer/latest',\n    uses_with={\n      'dim': 256,\n      'columns': columns,\n      'metric': 'cosine'\n    },\n    uses_metas={'workspace': '/my/tmp_folder'},\n    install_requirements=True\n)\n\nsearch_filter = {\"price\": {\"$lte\": 50}}\nwith f:\n    f.post(on='/index', inputs=docs)\n    query_res = f.post(on='/search',\n                       inputs=query_docs,\n                       return_results=True,\n                       parameters={'filter': search_filter})\n```\n\n## CRUD operations\n\nYou can perform all the usual operations on the respective endpoints\n\n- `/index` Add documents\n- `/search` Search with query documents.\n- `/update` Update documents\n- `/delete` Delete documents\n- `/clear` Clear the index\n- `/status` Return the status of index\n  - `total_docs`: the total number of indexed documents\n  - `dim`: the dimension of the embeddings\n  - `metric`: the distance metric type\n  - `is_trained`: whether the index is already trained\n"
  },
  {
    "path": "executor/benchmark.py",
    "content": "import tempfile\nimport time\n\nimport numpy as np\nfrom jina import DocumentArray\nfrom jina.logging.profile import TimeContext\n\nn_index = [10_000, 100_000, 500_000, 1_000_000]\n\nn_query = [1, 8, 64]\nD = 768\nR = 5\nB = 4096\nn_cells = 1\n\nfrom annlite.executor import AnnLiteIndexer\n\ntimes = {}\n\nfor n_i in n_index:\n    with tempfile.TemporaryDirectory() as tempdir:\n        idxer = AnnLiteIndexer(\n            n_dim=D,\n            initial_size=n_i,\n            n_cells=n_cells,\n            data_path=str(tempdir),\n        )\n\n        # build index docs\n        i_embs = np.random.random([n_i, D]).astype(np.float32)\n\n        if n_cells > 1:\n            idxer._index.vq_codec.fit(i_embs)\n\n        da = DocumentArray.empty(n_i)\n        da.embeddings = i_embs\n\n        with TimeContext(f'indexing {n_i} docs') as t_i:\n            for _batch in da.batch(batch_size=B):\n                idxer.index(_batch)\n\n        times[n_i] = {}\n        times[n_i]['index'] = t_i.duration\n\n        # waiting for the index to be ready\n        time.sleep(5)\n\n        for n_q in n_query:\n            q_embs = np.random.random([n_q, D]).astype(np.float32)\n            qa = DocumentArray.empty(n_q)\n            qa.embeddings = q_embs\n\n            t_qs = []\n            for _ in range(R):\n                with TimeContext(f'searching {n_q} docs') as t_q:\n                    idxer.search(qa)\n                t_qs.append(t_q.duration)\n                # # check if it return the full doc\n                # assert qa[0].matches\n                # assert qa[0].matches.embeddings.shape\n            times[n_i][f'query_{n_q}'] = np.mean(t_qs[1:])  # remove warm-up\n\n        idxer.clear()\n        idxer.close()\n\nprint('|Stored data| Indexing time | Query size=1 | Query size=8 | Query size=64|')\nprint('|---' * (len(list(times.values())[0]) + 1) + '|')\nfor k, v in times.items():\n    s = ' | '.join(f'{v[vv]:.3f}' for vv in ['index', 'query_1', 'query_8', 'query_64'])\n    print(f'|{k} | {s}|')\n"
  },
  {
    "path": "executor/config.yml",
    "content": "jtype: AnnLiteIndexer\npy_modules:\n  - ./executor.py\nmetas:\n  name: AnnLiteIndexer_v3\n  description: A similarity search based on Annlite\n  url: https://github.com/jina-ai/annlite\n  keywords: [ann, similarity_search, indexer, pq, hnsw, pre-filtering]\n"
  },
  {
    "path": "executor/executor.py",
    "content": "import threading\nimport time\nimport traceback\nimport warnings\nfrom threading import Thread\nfrom typing import Dict, List, Optional, Tuple, Union\n\nfrom docarray import Document, DocumentArray\nfrom jina import Executor, requests\nfrom jina.logging.logger import JinaLogger\n\nINDEX_BATCH_SIZE = 1024\n\n\nclass AnnLiteIndexer(Executor):\n    \"\"\"A simple indexer that wraps the AnnLite indexer and adds a simple interface for indexing and searching.\n\n    :param n_dim: Dimensionality of vectors to index\n    :param metric: Distance metric type. Can be 'euclidean', 'inner_product', or 'cosine'\n    :param limit: Number of results to get for each query document in search\n    :param n_components: Number of components to use for dimensionality reduction\n    :param match_args: the arguments to `DocumentArray`'s match function\n    :param data_path: the workspace of the AnnLiteIndexer but not support when shards > 1.\n    :param ef_construction: The construction time/accuracy trade-off\n    :param ef_search: The query time accuracy/speed trade-off\n    :param max_connection: The maximum number of outgoing connections in the\n        graph (the \"M\" parameter)\n    :param include_metadata: If True, return the document metadata in response\n    :param index_access_paths: Default traversal paths on docs\n            (used for indexing, delete and update), e.g. '@r', '@c', '@r,c'\n    :param search_access_paths: Default traversal paths on docs\n    (used for search), e.g. '@r', '@c', '@r,c'\n    :param columns: A list or dict of column names to index.\n    :param dim: Deprecated, use n_dim instead\n    \"\"\"\n\n    def __init__(\n        self,\n        n_dim: int = 0,\n        metric: str = 'cosine',\n        limit: int = 10,\n        n_components: Optional[int] = None,\n        match_args: Optional[Dict] = None,\n        data_path: Optional[str] = None,\n        ef_construction: Optional[int] = None,\n        ef_search: Optional[int] = None,\n        max_connection: Optional[int] = None,\n        include_metadata: bool = True,\n        index_access_paths: str = '@r',\n        search_access_paths: str = '@r',\n        columns: Optional[Union[List[Tuple[str, str]], Dict[str, str]]] = None,\n        dim: int = None,\n        *args,\n        **kwargs,\n    ):\n\n        super().__init__(*args, **kwargs)\n        self.logger = JinaLogger(self.__class__.__name__)\n\n        n_dim = n_dim or dim\n        if not n_dim:\n            raise ValueError('Please specify the dimension of the vectors to index!')\n\n        self.n_components = n_components\n        self.metric = metric\n        self.match_args = match_args or {}\n        self.include_metadata = include_metadata\n        if limit:\n            self.match_args.update({'limit': limit})\n\n        self.index_access_paths = index_access_paths\n        if 'index_traversal_paths' in kwargs:\n            warnings.warn(\n                f'`index_traversal_paths` is deprecated. Use `index_access_paths` instead.'\n            )\n            self.index_access_paths = kwargs['index_traversal_paths']\n\n        self.search_access_paths = search_access_paths\n        if 'search_traversal_paths' in kwargs:\n            warnings.warn(\n                f'`search_traversal_paths` is deprecated. Use `search_access_paths` instead.'\n            )\n            self.search_access_paths = kwargs['search_traversal_paths']\n\n        self._data_buffer = DocumentArray()\n        self._index_batch_size = INDEX_BATCH_SIZE\n        self._max_length_queue = 2 * self._index_batch_size\n        self._index_lock = threading.Lock()\n\n        self.logger = JinaLogger(getattr(self.metas, 'name', self.__class__.__name__))\n\n        if getattr(self.runtime_args, 'shards', 1) > 1 and data_path:\n            raise ValueError(\n                '`data_path` is not supported when shards > 1, please use `workspace` instead'\n            )\n\n        config = {\n            'n_dim': n_dim,\n            'n_components': n_components,\n            'metric': metric,\n            'ef_construction': ef_construction,\n            'ef_search': ef_search,\n            'max_connection': max_connection,\n            'data_path': data_path or self.workspace or './workspace',\n            'columns': columns,\n        }\n        self._index = DocumentArray(storage='annlite', config=config)\n\n        # start indexing thread in background to group indexing requests\n        # together and perform batch indexing at once\n        self._start_index_loop()\n\n    @requests(on='/index')\n    def index(\n        self, docs: Optional[DocumentArray] = None, parameters: dict = {}, **kwargs\n    ):\n        \"\"\"Index new documents\n\n        :param docs: the Documents to index\n        :param parameters: dictionary with options for indexing\n        Keys accepted:\n            - 'access_paths': traversal paths on docs, e.g. '@r', '@c', '@r,c'\n        \"\"\"\n\n        if not docs:\n            return\n\n        access_paths = parameters.get('access_paths', self.index_access_paths)\n        flat_docs = docs[access_paths]\n        if len(flat_docs) == 0:\n            return\n\n        while len(self._data_buffer) >= self._max_length_queue:\n            time.sleep(0.001)\n\n        with self._index_lock:\n            self._data_buffer.extend(flat_docs)\n\n    def _start_index_loop(self):\n        \"\"\"Start the indexing loop in background.\n\n        This loop is responsible for batch indexing the documents in the buffer.\n        \"\"\"\n\n        def _index_loop():\n            try:\n                while True:\n                    # if the buffer is none, will break the loop\n                    if self._data_buffer is None:\n                        break\n\n                    # if the buffer is empty, will wait for new documents to be added\n                    if len(self._data_buffer) == 0:\n                        time.sleep(0.1)  # sleep for 100ms\n                        continue\n\n                    # acquire the lock to prevent threading issues\n                    with self._index_lock:\n                        batch_docs = self._data_buffer.pop(\n                            range(\n                                self._index_batch_size\n                                if len(self._data_buffer) > self._index_batch_size\n                                else len(self._data_buffer)\n                            )\n                        )\n                        self._index.extend(batch_docs)\n                        self.logger.debug(f'indexing {len(batch_docs)} docs done...')\n            except Exception as e:\n                self.logger.error(traceback.format_exc())\n                raise e\n\n        self._index_thread = Thread(target=_index_loop, daemon=False)\n        self._index_thread.start()\n\n    @requests(on='/update')\n    def update(\n        self, docs: Optional[DocumentArray] = None, parameters: dict = {}, **kwargs\n    ):\n        \"\"\"Update existing documents\n\n        :param docs: the Documents to update\n        :param parameters: dictionary with options for updating\n        Keys accepted:\n            - 'access_paths': traversal paths on docs, e.g. '@r', '@c', '@r,c'\n            - 'raise_errors_on_not_found': if True, raise an error if a document is not found. Default is False.\n        \"\"\"\n\n        if not docs:\n            return\n\n        access_paths = parameters.get('access_paths', self.index_access_paths)\n        raise_errors_on_not_found = parameters.get('raise_errors_on_not_found', False)\n        flat_docs = docs[access_paths]\n        if len(flat_docs) == 0:\n            return\n\n        with self._index_lock:\n            if len(self._data_buffer) > 0:\n                raise RuntimeError(\n                    f'Cannot update documents while the pending documents in the buffer are not indexed yet. '\n                    'Please wait for the pending documents to be indexed.'\n                )\n            for doc in flat_docs:\n                try:\n                    self._index[doc.id] = doc\n                except IndexError:\n                    if raise_errors_on_not_found:\n                        raise Exception(\n                            f'The document (id={doc.id}) cannot be updated as'\n                            f'it is not found in the index'\n                        )\n                    else:\n                        self.logger.warning(\n                            f'cannot update doc {doc.id} as it does not exist in storage'\n                        )\n\n    @requests(on='/delete')\n    def delete(self, parameters: dict = {}, **kwargs):\n        \"\"\"Delete existing documents\n\n        Delete entries from the index by id\n        :param parameters: parameters to the request\n        \"\"\"\n\n        delete_ids = parameters.get('ids', [])\n        if len(delete_ids) == 0:\n            return\n\n        with self._index_lock:\n            if len(self._data_buffer) > 0:\n                raise RuntimeError(\n                    f'Cannot delete documents while the pending documents in the buffer are not indexed yet. '\n                    'Please wait for the pending documents to be indexed.'\n                )\n\n            del self._index[delete_ids]\n\n    @requests(on='/search')\n    def search(\n        self, docs: Optional[DocumentArray] = None, parameters: dict = {}, **kwargs\n    ):\n        \"\"\"Perform a vector similarity search and retrieve Document matches\n\n        Search can be performed with candidate filtering. Filters are a triplet (column,operator,value).\n        More than a filter can be applied during search. Therefore, conditions for a filter are specified as a list triplets.\n        Each triplet contains:\n\n        - column: Column used to filter.\n        - operator: Binary operation between two values. Some supported operators include `['>','<','=','<=','>=']`.\n        - value: value used to compare a candidate.\n\n        :param docs: the Documents to search with\n        :param parameters: dictionary for parameters for the search operation\n        Keys accepted:\n            - 'access_paths' (str): traversal paths on docs, e.g. '@r', '@c', '@r,c'\n            - 'filter' (dict): the filtering conditions on document tags\n            - 'limit' (int): nr of matches to get per Document\n        \"\"\"\n\n        if not docs:\n            return\n\n        access_paths = parameters.get('access_paths', self.search_access_paths)\n        flat_docs = docs[access_paths]\n        match_args = (\n            {**self.match_args, **parameters}\n            if parameters is not None\n            else self.match_args\n        )\n\n        with self._index_lock:\n            # if len(self._data_buffer) > 0:\n            #     raise RuntimeError(\n            #         f'Cannot search documents while the pending documents in the buffer are not indexed yet. '\n            #         'Please wait for the pending documents to be indexed.'\n            #     )\n\n            flat_docs.match(self._index, **match_args)\n\n    @requests(on='/backup')\n    def backup(self, parameters: Optional[Dict] = {}, **kwargs):\n        \"\"\"\n        Backup data to local or remote.\n        Use api of <class 'annlite.index.AnnLite'>\n\n        Keys accepted:\n            - 'target' (str): the name of indexer you want to backup as\n        \"\"\"\n\n        target_name = parameters.get('target_name', None)\n        token = parameters.get('token', None)\n        if target_name:\n            target_name = f'{target_name}_{self.runtime_args.shard_id}'\n        with self._index_lock:\n            if len(self._data_buffer) > 0:\n                raise RuntimeError(\n                    f'Cannot backup documents while the pending documents in the buffer are not indexed yet. '\n                    'Please wait for the pending documents to be indexed.'\n                )\n            self._index._annlite.backup(target_name, token)\n\n    @requests(on='/restore')\n    def restore(self, parameters: Optional[Dict] = {}, **kwargs):\n        \"\"\"\n        Restore data from local or remote.\n        Use api of <class 'annlite.index.AnnLite'>\n        \"\"\"\n        source_name = parameters.get('source_name', None)\n        token = parameters.get('token', None)\n        if source_name:\n            source_name = f'{source_name}_{self.runtime_args.shard_id}'\n        self._index._annlite.restore(source_name, token)\n\n    @requests(on='/filter')\n    def filter(self, parameters: Dict, **kwargs):\n        \"\"\"\n        Query documents from the indexer by the filter `query` object in parameters. The `query` object must follow the\n        specifications in the `find` method of `DocumentArray` using annlite: https://docarray.jina.ai/fundamentals/documentarray/find/#filter-with-query-operators\n        :param parameters: Dictionary to define the `filter` that you want to use.\n        \"\"\"\n\n        return self._index.find(parameters.get('filter', None))\n\n    @requests(on='/fill_embedding')\n    def fill_embedding(self, docs: DocumentArray, **kwargs):\n        \"\"\"\n        retrieve embedding of Documents by id\n        :param docs: DocumentArray to search with\n        \"\"\"\n\n        for doc in docs:\n            doc.embedding = self._index[doc.id].embedding\n\n    @requests(on='/status')\n    def status(self, **kwargs) -> DocumentArray:\n        \"\"\"Return the document containing status information about the indexer.\n\n        The status will contain information on the total number of indexed and deleted\n        documents, and on the number of (searchable) documents currently in the index.\n        \"\"\"\n\n        status = Document(\n            tags={\n                'appending_size': len(self._data_buffer),\n                'total_docs': len(self._index),\n                'index_size': len(self._index),\n            }\n        )\n        return DocumentArray([status])\n\n    def flush(self):\n        \"\"\"Flush all the data in the buffer to the index\"\"\"\n        while len(self._data_buffer) > 0:\n            time.sleep(0.1)\n\n    @requests(on='/clear')\n    def clear(self, **kwargs):\n        \"\"\"Clear the index of all entries.\"\"\"\n        self.flush()\n\n        with self._index_lock:\n            self._data_buffer = None\n            self._index_thread.join()\n\n        self._data_buffer = DocumentArray()\n        self._index.clear()\n\n        self._start_index_loop()\n\n    def close(self, **kwargs):\n        \"\"\"Close the index.\"\"\"\n        super().close()\n\n        self.flush()\n\n        # wait for the index thread to finish\n        with self._index_lock:\n            self._data_buffer = None\n            self._index_thread.join()\n\n        # WARNING: the commented code below hangs the close in pytest `pytest tests/test_*.py`\n        # But don't know why. It works fine in `pytest tests/test_executor.py` and normal python execution\n        del self._index\n"
  },
  {
    "path": "executor/requirements.txt",
    "content": "annlite\ncertifi\ndocarray\n"
  },
  {
    "path": "include/hnswlib/bruteforce.h",
    "content": "#pragma once\n#include <algorithm>\n#include <fstream>\n#include <mutex>\n#include <unordered_map>\n\nnamespace hnswlib {\n    template<typename dist_t>\n    class BruteforceSearch : public AlgorithmInterface<dist_t> {\n    public:\n        BruteforceSearch(SpaceInterface <dist_t> *s) {\n\n        }\n        BruteforceSearch(SpaceInterface<dist_t> *s, const std::string &location) {\n            loadIndex(location, s);\n        }\n\n        BruteforceSearch(SpaceInterface <dist_t> *s, size_t maxElements) {\n            maxelements_ = maxElements;\n            data_size_ = s->get_data_size();\n            fstdistfunc_ = s->get_dist_func();\n            dist_func_param_ = s->get_dist_func_param();\n            size_per_element_ = data_size_ + sizeof(labeltype);\n            data_ = (char *) malloc(maxElements * size_per_element_);\n            if (data_ == nullptr)\n                std::runtime_error(\"Not enough memory: BruteforceSearch failed to allocate data\");\n            cur_element_count = 0;\n        }\n\n        ~BruteforceSearch() {\n            free(data_);\n        }\n\n        char *data_;\n        size_t maxelements_;\n        size_t cur_element_count;\n        size_t size_per_element_;\n\n        size_t data_size_;\n        DISTFUNC <dist_t> fstdistfunc_;\n        void *dist_func_param_;\n        std::mutex index_lock;\n\n        std::unordered_map<labeltype,size_t > dict_external_to_internal;\n\n        void addPoint(const void *datapoint, labeltype label) {\n\n            int idx;\n            {\n                std::unique_lock<std::mutex> lock(index_lock);\n\n\n\n                auto search=dict_external_to_internal.find(label);\n                if (search != dict_external_to_internal.end()) {\n                    idx=search->second;\n                }\n                else{\n                    if (cur_element_count >= maxelements_) {\n                        throw std::runtime_error(\"The number of elements exceeds the specified limit\\n\");\n                    }\n                    idx=cur_element_count;\n                    dict_external_to_internal[label] = idx;\n                    cur_element_count++;\n                }\n            }\n            memcpy(data_ + size_per_element_ * idx + data_size_, &label, sizeof(labeltype));\n            memcpy(data_ + size_per_element_ * idx, datapoint, data_size_);\n\n\n\n\n        };\n\n        void removePoint(labeltype cur_external) {\n            size_t cur_c=dict_external_to_internal[cur_external];\n\n            dict_external_to_internal.erase(cur_external);\n\n            labeltype label=*((labeltype*)(data_ + size_per_element_ * (cur_element_count-1) + data_size_));\n            dict_external_to_internal[label]=cur_c;\n            memcpy(data_ + size_per_element_ * cur_c,\n                   data_ + size_per_element_ * (cur_element_count-1),\n                   data_size_+sizeof(labeltype));\n            cur_element_count--;\n\n        }\n\n\n        std::priority_queue<std::pair<dist_t, labeltype>>\n        searchKnn(const void *query_data, size_t k, size_t batch_index) const {\n            std::priority_queue<std::pair<dist_t, labeltype>> topResults;\n            if (cur_element_count == 0) return topResults;\n            for (int i = 0; i < k; i++) {\n                dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_, nullptr);\n                topResults.push(std::pair<dist_t, labeltype>(dist, *((labeltype *)(data_ + size_per_element_ * i +\n                                                                                data_size_))));\n            }\n            dist_t lastdist = topResults.top().first;\n            for (int i = k; i < cur_element_count; i++) {\n                dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_, nullptr);\n                if (dist <= lastdist) {\n                    topResults.push(std::pair<dist_t, labeltype>(dist, *((labeltype *) (data_ + size_per_element_ * i +\n                                                                                        data_size_))));\n                    if (topResults.size() > k)\n                        topResults.pop();\n                    lastdist = topResults.top().first;\n                }\n\n            }\n            return topResults;\n        };\n\n        void saveIndex(const std::string &location) {\n            std::ofstream output(location, std::ios::binary);\n            std::streampos position;\n\n            writeBinaryPOD(output, maxelements_);\n            writeBinaryPOD(output, size_per_element_);\n            writeBinaryPOD(output, cur_element_count);\n\n            output.write(data_, maxelements_ * size_per_element_);\n\n            output.close();\n        }\n\n        void loadIndex(const std::string &location, SpaceInterface<dist_t> *s) {\n\n\n            std::ifstream input(location, std::ios::binary);\n            std::streampos position;\n\n            readBinaryPOD(input, maxelements_);\n            readBinaryPOD(input, size_per_element_);\n            readBinaryPOD(input, cur_element_count);\n\n            data_size_ = s->get_data_size();\n            fstdistfunc_ = s->get_dist_func();\n            dist_func_param_ = s->get_dist_func_param();\n            size_per_element_ = data_size_ + sizeof(labeltype);\n            data_ = (char *) malloc(maxelements_ * size_per_element_);\n            if (data_ == nullptr)\n                std::runtime_error(\"Not enough memory: loadIndex failed to allocate data\");\n\n            input.read(data_, maxelements_ * size_per_element_);\n\n            input.close();\n\n        }\n\n    };\n}\n"
  },
  {
    "path": "include/hnswlib/fusefilter.h",
    "content": "#ifndef BINARYFUSEFILTER_H\n#define BINARYFUSEFILTER_H\n#include <math.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef XOR_MAX_ITERATIONS\n#define XOR_MAX_ITERATIONS                                                     \\\n  100 // probabillity of success should always be > 0.5 so 100 iterations is\n      // highly unlikely\n#endif\n#ifdef _MSC_VER\n#include <cstdio>\n#include <intrin.h>\n#endif\n\n/**\n * We start with a few utilities.\n ***/\nstatic inline uint64_t binary_fuse_murmur64(uint64_t h) {\n  h ^= h >> 33;\n  h *= UINT64_C(0xff51afd7ed558ccd);\n  h ^= h >> 33;\n  h *= UINT64_C(0xc4ceb9fe1a85ec53);\n  h ^= h >> 33;\n  return h;\n}\nstatic inline uint64_t binary_fuse_mix_split(uint64_t key, uint64_t seed) {\n  return binary_fuse_murmur64(key + seed);\n}\nstatic inline uint64_t binary_fuse_rotl64(uint64_t n, unsigned int c) {\n  return (n << (c & 63)) | (n >> ((-c) & 63));\n}\nstatic inline uint32_t binary_fuse_reduce(uint32_t hash, uint32_t n) {\n  // http://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/\n  return (uint32_t)(((uint64_t)hash * n) >> 32);\n}\nstatic inline uint64_t binary_fuse8_fingerprint(uint64_t hash) {\n  return hash ^ (hash >> 32);\n}\n\n/**\n * We need a decent random number generator.\n **/\n\n// returns random number, modifies the seed\nstatic inline uint64_t binary_fuse_rng_splitmix64(uint64_t *seed) {\n  uint64_t z = (*seed += UINT64_C(0x9E3779B97F4A7C15));\n  z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);\n  z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB);\n  return z ^ (z >> 31);\n}\n\ntypedef struct binary_fuse8_s {\n  uint64_t Seed;\n  uint32_t SegmentLength;\n  uint32_t SegmentLengthMask;\n  uint32_t SegmentCount;\n  uint32_t SegmentCountLength;\n  uint32_t ArrayLength;\n  uint8_t *Fingerprints;\n} binary_fuse8_t;\n\n#ifdef _MSC_VER\n// Windows programmers who target 32-bit platform may need help:\nstatic inline uint64_t binary_fuse_mulhi(uint64_t a, uint64_t b) { return __umulh(a, b); }\n#else\nstatic inline uint64_t binary_fuse_mulhi(uint64_t a, uint64_t b) {\n  return ((__uint128_t)a * b) >> 64;\n}\n#endif\n\ntypedef struct binary_hashes_s {\n  uint32_t h0;\n  uint32_t h1;\n  uint32_t h2;\n} binary_hashes_t;\n\nstatic inline binary_hashes_t binary_fuse8_hash_batch(uint64_t hash,\n                                        const binary_fuse8_t *filter) {\n  uint64_t hi = binary_fuse_mulhi(hash, filter->SegmentCountLength);\n  binary_hashes_t ans;\n  ans.h0 = (uint32_t)hi;\n  ans.h1 = ans.h0 + filter->SegmentLength;\n  ans.h2 = ans.h1 + filter->SegmentLength;\n  ans.h1 ^= (uint32_t)(hash >> 18) & filter->SegmentLengthMask;\n  ans.h2 ^= (uint32_t)(hash)&filter->SegmentLengthMask;\n  return ans;\n}\nstatic inline uint32_t binary_fuse8_hash(int index, uint64_t hash,\n                                         const binary_fuse8_t *filter) {\n  uint64_t h = binary_fuse_mulhi(hash, filter->SegmentCountLength);\n  h += index * filter->SegmentLength;\n  // keep the lower 36 bits\n  uint64_t hh = hash & ((1UL << 36) - 1);\n  // index 0: right shift by 36; index 1: right shift by 18; index 2: no shift\n  h ^= (size_t)((hh >> (36 - 18 * index)) & filter->SegmentLengthMask);\n  return h;\n}\n\n// Report if the key is in the set, with false positive rate.\nstatic inline bool binary_fuse8_contain(uint64_t key,\n                                        const binary_fuse8_t *filter) {\n  uint64_t hash = binary_fuse_mix_split(key, filter->Seed);\n  uint8_t f = binary_fuse8_fingerprint(hash);\n  binary_hashes_t hashes = binary_fuse8_hash_batch(hash, filter);\n  f ^= filter->Fingerprints[hashes.h0] ^ filter->Fingerprints[hashes.h1] ^\n       filter->Fingerprints[hashes.h2];\n  return f == 0;\n}\n\nstatic inline uint32_t binary_fuse_calculate_segment_length(uint32_t arity,\n                                                            uint32_t size) {\n  // These parameters are very sensitive. Replacing 'floor' by 'round' can\n  // substantially affect the construction time.\n  if (arity == 3) {\n    return ((uint32_t)1) << (int)(floor(log((double)(size)) / log(3.33) + 2.25));\n  } else if (arity == 4) {\n    return ((uint32_t)1) << (int)(floor(log((double)(size)) / log(2.91) - 0.5));\n  } else {\n    return 65536;\n  }\n}\n\nstatic inline double binary_fuse8_max(double a, double b) {\n  if (a < b) {\n    return b;\n  }\n  return a;\n}\n\nstatic inline double binary_fuse_calculate_size_factor(uint32_t arity,\n                                                       uint32_t size) {\n  if (arity == 3) {\n    return binary_fuse8_max(1.125, 0.875 + 0.25 * log(1000000.0) / log((double)size));\n  } else if (arity == 4) {\n    return binary_fuse8_max(1.075, 0.77 + 0.305 * log(600000.0) / log((double)size));\n  } else {\n    return 2.0;\n  }\n}\n\n// allocate enough capacity for a set containing up to 'size' elements\n// caller is responsible to call binary_fuse8_free(filter)\n// size should be at least 2.\nstatic inline bool binary_fuse8_allocate(uint32_t size,\n                                         binary_fuse8_t *filter) {\n  uint32_t arity = 3;\n  filter->SegmentLength = size == 0 ? 4 : binary_fuse_calculate_segment_length(arity, size);\n  if (filter->SegmentLength > 262144) {\n    filter->SegmentLength = 262144;\n  }\n  filter->SegmentLengthMask = filter->SegmentLength - 1;\n  double sizeFactor = binary_fuse_calculate_size_factor(arity, size);\n  uint32_t capacity = size <= 1 ? 0 : (uint32_t)(round((double)size * sizeFactor));\n  uint32_t initSegmentCount =\n      (capacity + filter->SegmentLength - 1) / filter->SegmentLength -\n      (arity - 1);\n  filter->ArrayLength = (initSegmentCount + arity - 1) * filter->SegmentLength;\n  filter->SegmentCount =\n      (filter->ArrayLength + filter->SegmentLength - 1) / filter->SegmentLength;\n  if (filter->SegmentCount <= arity - 1) {\n    filter->SegmentCount = 1;\n  } else {\n    filter->SegmentCount = filter->SegmentCount - (arity - 1);\n  }\n  filter->ArrayLength =\n      (filter->SegmentCount + arity - 1) * filter->SegmentLength;\n  filter->SegmentCountLength = filter->SegmentCount * filter->SegmentLength;\n  filter->Fingerprints = (uint8_t *)malloc(filter->ArrayLength);\n  return filter->Fingerprints != NULL;\n}\n\n// report memory usage\nstatic inline size_t binary_fuse8_size_in_bytes(const binary_fuse8_t *filter) {\n  return filter->ArrayLength * sizeof(uint8_t) + sizeof(binary_fuse8_t);\n}\n\n// release memory\nstatic inline void binary_fuse8_free(binary_fuse8_t *filter) {\n  free(filter->Fingerprints);\n  filter->Fingerprints = NULL;\n  filter->Seed = 0;\n  filter->SegmentLength = 0;\n  filter->SegmentLengthMask = 0;\n  filter->SegmentCount = 0;\n  filter->SegmentCountLength = 0;\n  filter->ArrayLength = 0;\n}\n\nstatic inline uint8_t binary_fuse_mod3(uint8_t x) {\n    return x > 2 ? x - 3 : x;\n}\n\n// construct the filter, returns true on success, false on failure.\n// most likely, a failure is due to too high a memory usage\n// size is the number of keys\n// The caller is responsable for calling binary_fuse8_allocate(size,filter)\n// before. The caller is responsible to ensure that there are not too many  duplicated\n// keys. The inner loop will run up to XOR_MAX_ITERATIONS times (default on\n// 100), it should never fail, except if there are many duplicated keys. If it fails,\n// a return value of false is provided.\n//\n//\n// If there are many duplicated keys and you do not want to remove them, you can first\n// sort your input, the algorithm will then work adequately.\nbool binary_fuse8_populate(const uint64_t *keys, uint32_t size,\n                           binary_fuse8_t *filter) {\n  uint64_t rng_counter = 0x726b2b9d438b9d4d;\n  filter->Seed = binary_fuse_rng_splitmix64(&rng_counter);\n  uint64_t *reverseOrder = (uint64_t *)calloc((size + 1), sizeof(uint64_t));\n  uint32_t capacity = filter->ArrayLength;\n  uint32_t *alone = (uint32_t *)malloc(capacity * sizeof(uint32_t));\n  uint8_t *t2count = (uint8_t *)calloc(capacity, sizeof(uint8_t));\n  uint8_t *reverseH = (uint8_t *)malloc(size * sizeof(uint8_t));\n  uint64_t *t2hash = (uint64_t *)calloc(capacity, sizeof(uint64_t));\n\n  uint32_t blockBits = 1;\n  while (((uint32_t)1 << blockBits) < filter->SegmentCount) {\n    blockBits += 1;\n  }\n  uint32_t block = ((uint32_t)1 << blockBits);\n  uint32_t *startPos = (uint32_t *)malloc((1 << blockBits) * sizeof(uint32_t));\n  uint32_t h012[5];\n\n  if ((alone == NULL) || (t2count == NULL) || (reverseH == NULL) ||\n      (t2hash == NULL) || (reverseOrder == NULL) || (startPos == NULL)) {\n    free(alone);\n    free(t2count);\n    free(reverseH);\n    free(t2hash);\n    free(reverseOrder);\n    free(startPos);\n    return false;\n  }\n  reverseOrder[size] = 1;\n  for (int loop = 0; true; ++loop) {\n    if (loop + 1 > XOR_MAX_ITERATIONS) {\n      fprintf(stderr, \"Too many iterations. Are all your keys unique?\");\n      free(alone);\n      free(t2count);\n      free(reverseH);\n      free(t2hash);\n      free(reverseOrder);\n      free(startPos);\n      return false;\n    }\n\n    for (uint32_t i = 0; i < block; i++) {\n      // important : i * size would overflow as a 32-bit number in some\n      // cases.\n      startPos[i] = ((uint64_t)i * size) >> blockBits;\n    }\n\n    uint64_t maskblock = block - 1;\n    for (uint32_t i = 0; i < size; i++) {\n      uint64_t hash = binary_fuse_murmur64(keys[i] + filter->Seed);\n      uint64_t segment_index = hash >> (64 - blockBits);\n      while (reverseOrder[startPos[segment_index]] != 0) {\n        segment_index++;\n        segment_index &= maskblock;\n      }\n      reverseOrder[startPos[segment_index]] = hash;\n      startPos[segment_index]++;\n    }\n    int error = 0;\n    uint32_t duplicates = 0;\n    for (uint32_t i = 0; i < size; i++) {\n      uint64_t hash = reverseOrder[i];\n      uint32_t h0 = binary_fuse8_hash(0, hash, filter);\n      t2count[h0] += 4;\n      t2hash[h0] ^= hash;\n      uint32_t h1 = binary_fuse8_hash(1, hash, filter);\n      t2count[h1] += 4;\n      t2count[h1] ^= 1;\n      t2hash[h1] ^= hash;\n      uint32_t h2 = binary_fuse8_hash(2, hash, filter);\n      t2count[h2] += 4;\n      t2hash[h2] ^= hash;\n      t2count[h2] ^= 2;\n      if ((t2hash[h0] & t2hash[h1] & t2hash[h2]) == 0) {\n        if (((t2hash[h0] == 0) && (t2count[h0] == 8)) ||\n            ((t2hash[h1] == 0) && (t2count[h1] == 8)) ||\n            ((t2hash[h2] == 0) && (t2count[h2] == 8))) {\n          duplicates += 1;\n          t2count[h0] -= 4;\n          t2hash[h0] ^= hash;\n          t2count[h1] -= 4;\n          t2count[h1] ^= 1;\n          t2hash[h1] ^= hash;\n          t2count[h2] -= 4;\n          t2count[h2] ^= 2;\n          t2hash[h2] ^= hash;\n        }\n      }\n      error = (t2count[h0] < 4) ? 1 : error;\n      error = (t2count[h1] < 4) ? 1 : error;\n      error = (t2count[h2] < 4) ? 1 : error;\n    }\n    if (error) {\n    memset(reverseOrder, 0, sizeof(uint64_t)*size);\n    memset(t2count, 0, sizeof(uint8_t)*capacity);\n    memset(t2hash, 0, sizeof(uint64_t)*capacity);\n      filter->Seed = binary_fuse_rng_splitmix64(&rng_counter);\n      continue;\n    }\n\n    // End of key addition\n    uint32_t Qsize = 0;\n    // Add sets with one key to the queue.\n    for (uint32_t i = 0; i < capacity; i++) {\n      alone[Qsize] = i;\n      Qsize += ((t2count[i] >> 2) == 1) ? 1 : 0;\n    }\n    uint32_t stacksize = 0;\n    while (Qsize > 0) {\n      Qsize--;\n      uint32_t index = alone[Qsize];\n      if ((t2count[index] >> 2) == 1) {\n        uint64_t hash = t2hash[index];\n\n        // h012[0] = binary_fuse8_hash(0, hash, filter);\n        h012[1] = binary_fuse8_hash(1, hash, filter);\n        h012[2] = binary_fuse8_hash(2, hash, filter);\n        h012[3] = binary_fuse8_hash(0, hash, filter); // == h012[0];\n        h012[4] = h012[1];\n        uint8_t found = t2count[index] & 3;\n        reverseH[stacksize] = found;\n        reverseOrder[stacksize] = hash;\n        stacksize++;\n        uint32_t other_index1 = h012[found + 1];\n        alone[Qsize] = other_index1;\n        Qsize += ((t2count[other_index1] >> 2) == 2 ? 1 : 0);\n\n        t2count[other_index1] -= 4;\n        t2count[other_index1] ^= binary_fuse_mod3(found + 1);\n        t2hash[other_index1] ^= hash;\n\n        uint32_t other_index2 = h012[found + 2];\n        alone[Qsize] = other_index2;\n        Qsize += ((t2count[other_index2] >> 2) == 2 ? 1 : 0);\n        t2count[other_index2] -= 4;\n        t2count[other_index2] ^= binary_fuse_mod3(found + 2);\n        t2hash[other_index2] ^= hash;\n      }\n    }\n    if (stacksize + duplicates == size) {\n      // success\n      size = stacksize;\n      break;\n    }\n    memset(reverseOrder, 0, sizeof(uint64_t)*size);\n    memset(t2count, 0, sizeof(uint8_t)*capacity);\n    memset(t2hash, 0, sizeof(uint64_t)*capacity);\n    filter->Seed = binary_fuse_rng_splitmix64(&rng_counter);\n  }\n\n  for (uint32_t i = size - 1; i < size; i--) {\n    // the hash of the key we insert next\n    uint64_t hash = reverseOrder[i];\n    uint8_t xor2 = binary_fuse8_fingerprint(hash);\n    uint8_t found = reverseH[i];\n    h012[0] = binary_fuse8_hash(0, hash, filter);\n    h012[1] = binary_fuse8_hash(1, hash, filter);\n    h012[2] = binary_fuse8_hash(2, hash, filter);\n    h012[3] = h012[0];\n    h012[4] = h012[1];\n    filter->Fingerprints[h012[found]] = xor2 ^\n                                        filter->Fingerprints[h012[found + 1]] ^\n                                        filter->Fingerprints[h012[found + 2]];\n  }\n  free(alone);\n  free(t2count);\n  free(reverseH);\n  free(t2hash);\n  free(reverseOrder);\n  free(startPos);\n  return true;\n}\n\n//////////////////\n// fuse16\n//////////////////\n\ntypedef struct binary_fuse16_s {\n  uint64_t Seed;\n  uint32_t SegmentLength;\n  uint32_t SegmentLengthMask;\n  uint32_t SegmentCount;\n  uint32_t SegmentCountLength;\n  uint32_t ArrayLength;\n  uint16_t *Fingerprints;\n\n  binary_fuse16_s(uint32_t size) {\n    uint32_t arity = 3;\n    SegmentLength =\n        size == 0 ? 4 : binary_fuse_calculate_segment_length(arity, size);\n    if (SegmentLength > 262144) {\n      SegmentLength = 262144;\n    }\n    SegmentLengthMask = SegmentLength - 1;\n    double sizeFactor =\n        size <= 1 ? 0 : binary_fuse_calculate_size_factor(arity, size);\n    uint32_t capacity = (uint32_t)(round((double)size * sizeFactor));\n    uint32_t initSegmentCount =\n        (capacity + SegmentLength - 1) / SegmentLength - (arity - 1);\n    ArrayLength = (initSegmentCount + arity - 1) * SegmentLength;\n    SegmentCount = (ArrayLength + SegmentLength - 1) / SegmentLength;\n    if (SegmentCount <= arity - 1) {\n      SegmentCount = 1;\n    } else {\n      SegmentCount = SegmentCount - (arity - 1);\n    }\n    ArrayLength = (SegmentCount + arity - 1) * SegmentLength;\n    SegmentCountLength = SegmentCount * SegmentLength;\n    Fingerprints = (uint16_t *)malloc(ArrayLength * sizeof(uint16_t));\n    memset(Fingerprints, 0, ArrayLength * sizeof(uint16_t));\n    if (Fingerprints == NULL) {\n      throw std::runtime_error(\"not enough memory to hold the fuse filter\");\n    }\n  }\n\n  ~binary_fuse16_s() {\n    free(Fingerprints);\n    Fingerprints = NULL;\n    Seed = 0;\n    SegmentLength = 0;\n    SegmentLengthMask = 0;\n    SegmentCount = 0;\n    SegmentCountLength = 0;\n    ArrayLength = 0;\n  }\n} binary_fuse16_t;\n\nstatic inline uint64_t binary_fuse16_fingerprint(uint64_t hash) {\n  return hash ^ (hash >> 32);\n}\n\nstatic inline binary_hashes_t binary_fuse16_hash_batch(uint64_t hash,\n                                        const binary_fuse16_t *filter) {\n  uint64_t hi = binary_fuse_mulhi(hash, filter->SegmentCountLength);\n  binary_hashes_t ans;\n  ans.h0 = (uint32_t)hi;\n  ans.h1 = ans.h0 + filter->SegmentLength;\n  ans.h2 = ans.h1 + filter->SegmentLength;\n  ans.h1 ^= (uint32_t)(hash >> 18) & filter->SegmentLengthMask;\n  ans.h2 ^= (uint32_t)(hash)&filter->SegmentLengthMask;\n  return ans;\n}\nstatic inline uint32_t binary_fuse16_hash(int index, uint64_t hash,\n                                          const binary_fuse16_t *filter) {\n  uint64_t h = binary_fuse_mulhi(hash, filter->SegmentCountLength);\n  h += index * filter->SegmentLength;\n  // keep the lower 36 bits\n  uint64_t hh = hash & ((1UL << 36) - 1);\n  // index 0: right shift by 36; index 1: right shift by 18; index 2: no shift\n  h ^= (size_t)((hh >> (36 - 18 * index)) & filter->SegmentLengthMask);\n  return h;\n}\n\n// Report if the key is in the set, with false positive rate.\nstatic inline bool binary_fuse16_contain(uint64_t key,\n                                         const binary_fuse16_t *filter) {\n  uint64_t hash = binary_fuse_mix_split(key, filter->Seed);\n  uint16_t f = binary_fuse16_fingerprint(hash);\n  binary_hashes_t hashes = binary_fuse16_hash_batch(hash, filter);\n  f ^= filter->Fingerprints[hashes.h0] ^ filter->Fingerprints[hashes.h1] ^\n       filter->Fingerprints[hashes.h2];\n  return f == 0;\n}\n\n// allocate enough capacity for a set containing up to 'size' elements\n// caller is responsible to call binary_fuse16_free(filter)\n// size should be at least 2.\nstatic inline bool binary_fuse16_allocate(uint32_t size,\n                                         binary_fuse16_t *filter) {\n  uint32_t arity = 3;\n  filter->SegmentLength = size == 0 ? 4 : binary_fuse_calculate_segment_length(arity, size);\n  if (filter->SegmentLength > 262144) {\n    filter->SegmentLength = 262144;\n  }\n  filter->SegmentLengthMask = filter->SegmentLength - 1;\n  double sizeFactor = size <= 1 ? 0 : binary_fuse_calculate_size_factor(arity, size);\n  uint32_t capacity = (uint32_t)(round((double)size * sizeFactor));\n  uint32_t initSegmentCount =\n      (capacity + filter->SegmentLength - 1) / filter->SegmentLength -\n      (arity - 1);\n  filter->ArrayLength = (initSegmentCount + arity - 1) * filter->SegmentLength;\n  filter->SegmentCount =\n      (filter->ArrayLength + filter->SegmentLength - 1) / filter->SegmentLength;\n  if (filter->SegmentCount <= arity - 1) {\n    filter->SegmentCount = 1;\n  } else {\n    filter->SegmentCount = filter->SegmentCount - (arity - 1);\n  }\n  filter->ArrayLength =\n      (filter->SegmentCount + arity - 1) * filter->SegmentLength;\n  filter->SegmentCountLength = filter->SegmentCount * filter->SegmentLength;\n  filter->Fingerprints = (uint16_t*)malloc(filter->ArrayLength * sizeof(uint16_t));\n  return filter->Fingerprints != NULL;\n}\n\n// report memory usage\nstatic inline size_t\nbinary_fuse16_size_in_bytes(const binary_fuse16_t *filter) {\n  return filter->ArrayLength * sizeof(uint16_t) + sizeof(binary_fuse16_t);\n}\n\n// release memory\nstatic inline void binary_fuse16_free(binary_fuse16_t *filter) {\n  free(filter->Fingerprints);\n  filter->Fingerprints = NULL;\n  filter->Seed = 0;\n  filter->SegmentLength = 0;\n  filter->SegmentLengthMask = 0;\n  filter->SegmentCount = 0;\n  filter->SegmentCountLength = 0;\n  filter->ArrayLength = 0;\n}\n\n// construct the filter, returns true on success, false on failure.\n// most likely, a failure is due to too high a memory usage\n// size is the number of keys\n// The caller is responsable for calling binary_fuse8_allocate(size,filter)\n// before. The caller is responsible to ensure that there are not too many duplicated\n// keys. The inner loop will run up to XOR_MAX_ITERATIONS times (default on\n// 100), it should never fail, except if there are many duplicated keys. If it fails,\n// a return value of false is provided.\n//\n// If there are many duplicated keys and you do not want to remove them, you can first\n// sort your input, the algorithm will then work adequately.\ninline bool binary_fuse16_populate(const uint64_t *keys, uint32_t size,\n                            binary_fuse16_t *filter) {\n  uint64_t rng_counter = 0x726b2b9d438b9d4d;\n  filter->Seed = binary_fuse_rng_splitmix64(&rng_counter);\n  uint64_t *reverseOrder = (uint64_t *)calloc((size + 1), sizeof(uint64_t));\n  uint32_t capacity = filter->ArrayLength;\n  uint32_t *alone = (uint32_t *)malloc(capacity * sizeof(uint32_t));\n  uint8_t *t2count = (uint8_t *)calloc(capacity, sizeof(uint8_t));\n  uint8_t *reverseH = (uint8_t *)malloc(size * sizeof(uint8_t));\n  uint64_t *t2hash = (uint64_t *)calloc(capacity, sizeof(uint64_t));\n\n  uint32_t blockBits = 1;\n  while (((uint32_t)1 << blockBits) < filter->SegmentCount) {\n    blockBits += 1;\n  }\n  uint32_t block = ((uint32_t)1 << blockBits);\n  uint32_t *startPos = (uint32_t *)malloc((1 << blockBits) * sizeof(uint32_t));\n  uint32_t h012[5];\n\n  if ((alone == NULL) || (t2count == NULL) || (reverseH == NULL) ||\n      (t2hash == NULL) || (reverseOrder == NULL) || (startPos == NULL)) {\n    free(alone);\n    free(t2count);\n    free(reverseH);\n    free(t2hash);\n    free(reverseOrder);\n    free(startPos);\n    return false;\n  }\n  reverseOrder[size] = 1;\n  for (int loop = 0; true; ++loop) {\n    if (loop + 1 > XOR_MAX_ITERATIONS) {\n      fprintf(stderr, \"Too many iterations. Are all your keys unique?\");\n      free(alone);\n      free(t2count);\n      free(reverseH);\n      free(t2hash);\n      free(reverseOrder);\n      free(startPos);\n      return false;\n    }\n\n    for (uint32_t i = 0; i < block; i++) {\n      // important : i * size would overflow as a 32-bit number in some\n      // cases.\n      startPos[i] = ((uint64_t)i * size) >> blockBits;\n    }\n\n    uint64_t maskblock = block - 1;\n    for (uint32_t i = 0; i < size; i++) {\n      uint64_t hash = binary_fuse_murmur64(keys[i] + filter->Seed);\n      uint64_t segment_index = hash >> (64 - blockBits);\n      while (reverseOrder[startPos[segment_index]] != 0) {\n        segment_index++;\n        segment_index &= maskblock;\n      }\n      reverseOrder[startPos[segment_index]] = hash;\n      startPos[segment_index]++;\n    }\n    int error = 0;\n    uint32_t duplicates = 0;\n    for (uint32_t i = 0; i < size; i++) {\n      uint64_t hash = reverseOrder[i];\n      uint32_t h0 = binary_fuse16_hash(0, hash, filter);\n      t2count[h0] += 4;\n      t2hash[h0] ^= hash;\n      uint32_t h1 = binary_fuse16_hash(1, hash, filter);\n      t2count[h1] += 4;\n      t2count[h1] ^= 1;\n      t2hash[h1] ^= hash;\n      uint32_t h2 = binary_fuse16_hash(2, hash, filter);\n      t2count[h2] += 4;\n      t2hash[h2] ^= hash;\n      t2count[h2] ^= 2;\n      if ((t2hash[h0] & t2hash[h1] & t2hash[h2]) == 0) {\n        if (((t2hash[h0] == 0) && (t2count[h0] == 8)) ||\n            ((t2hash[h1] == 0) && (t2count[h1] == 8)) ||\n            ((t2hash[h2] == 0) && (t2count[h2] == 8))) {\n          duplicates += 1;\n          t2count[h0] -= 4;\n          t2hash[h0] ^= hash;\n          t2count[h1] -= 4;\n          t2count[h1] ^= 1;\n          t2hash[h1] ^= hash;\n          t2count[h2] -= 4;\n          t2count[h2] ^= 2;\n          t2hash[h2] ^= hash;\n        }\n      }\n      error = (t2count[h0] < 4) ? 1 : error;\n      error = (t2count[h1] < 4) ? 1 : error;\n      error = (t2count[h2] < 4) ? 1 : error;\n    }\n    if (error) {\n    memset(reverseOrder, 0, sizeof(uint64_t)*size);\n    memset(t2count, 0, sizeof(uint8_t)*capacity);\n    memset(t2hash, 0, sizeof(uint64_t)*capacity);\n      filter->Seed = binary_fuse_rng_splitmix64(&rng_counter);\n      continue;\n    }\n\n    // End of key addition\n    uint32_t Qsize = 0;\n    // Add sets with one key to the queue.\n    for (uint32_t i = 0; i < capacity; i++) {\n      alone[Qsize] = i;\n      Qsize += ((t2count[i] >> 2) == 1) ? 1 : 0;\n    }\n    uint32_t stacksize = 0;\n    while (Qsize > 0) {\n      Qsize--;\n      uint32_t index = alone[Qsize];\n      if ((t2count[index] >> 2) == 1) {\n        uint64_t hash = t2hash[index];\n\n        // h012[0] = binary_fuse16_hash(0, hash, filter);\n        h012[1] = binary_fuse16_hash(1, hash, filter);\n        h012[2] = binary_fuse16_hash(2, hash, filter);\n        h012[3] = binary_fuse16_hash(0, hash, filter); // == h012[0];\n        h012[4] = h012[1];\n        uint8_t found = t2count[index] & 3;\n        reverseH[stacksize] = found;\n        reverseOrder[stacksize] = hash;\n        stacksize++;\n        uint32_t other_index1 = h012[found + 1];\n        alone[Qsize] = other_index1;\n        Qsize += ((t2count[other_index1] >> 2) == 2 ? 1 : 0);\n\n        t2count[other_index1] -= 4;\n        t2count[other_index1] ^= binary_fuse_mod3(found + 1);\n        t2hash[other_index1] ^= hash;\n\n        uint32_t other_index2 = h012[found + 2];\n        alone[Qsize] = other_index2;\n        Qsize += ((t2count[other_index2] >> 2) == 2 ? 1 : 0);\n        t2count[other_index2] -= 4;\n        t2count[other_index2] ^= binary_fuse_mod3(found + 2);\n        t2hash[other_index2] ^= hash;\n      }\n    }\n    if (stacksize + duplicates == size) {\n      // success\n      size = stacksize;\n      break;\n    }\n    memset(reverseOrder, 0, sizeof(uint64_t)*size);\n    memset(t2count, 0, sizeof(uint8_t)*capacity);\n    memset(t2hash, 0, sizeof(uint64_t)*capacity);\n    filter->Seed = binary_fuse_rng_splitmix64(&rng_counter);\n  }\n\n  for (uint32_t i = size - 1; i < size; i--) {\n    // the hash of the key we insert next\n    uint64_t hash = reverseOrder[i];\n    uint16_t xor2 = binary_fuse16_fingerprint(hash);\n    uint8_t found = reverseH[i];\n    h012[0] = binary_fuse16_hash(0, hash, filter);\n    h012[1] = binary_fuse16_hash(1, hash, filter);\n    h012[2] = binary_fuse16_hash(2, hash, filter);\n    h012[3] = h012[0];\n    h012[4] = h012[1];\n    filter->Fingerprints[h012[found]] = xor2 ^\n                                        filter->Fingerprints[h012[found + 1]] ^\n                                        filter->Fingerprints[h012[found + 2]];\n  }\n  free(alone);\n  free(t2count);\n  free(reverseH);\n  free(t2hash);\n  free(reverseOrder);\n  free(startPos);\n  return true;\n}\n\n#endif"
  },
  {
    "path": "include/hnswlib/hnswalg.h",
    "content": "#pragma once\n\n#include \"hnswlib.h\"\n#include \"visited_list_pool.h\"\n#include <assert.h>\n#include <atomic>\n#include <list>\n#include <random>\n#include <stdlib.h>\n#include <unordered_set>\n\nnamespace hnswlib {\n    typedef unsigned int tableint;\n    typedef unsigned int linklistsizeint;\n\n    template<typename dist_t>\n    class HierarchicalNSW : public AlgorithmInterface<dist_t> {\n    public:\n        static const tableint max_update_element_locks = 65536;\n        HierarchicalNSW(SpaceInterface<dist_t> *s) {\n        }\n\n        HierarchicalNSW(SpaceInterface<dist_t> *s, const std::string &location, bool nmslib = false, size_t max_elements=0) {\n            loadIndex(location, s, max_elements);\n        }\n\n        HierarchicalNSW(SpaceInterface<dist_t> *s, size_t max_elements, size_t M = 16, size_t ef_construction = 200, size_t random_seed = 100) :\n                link_list_locks_(max_elements), link_list_update_locks_(max_update_element_locks), element_levels_(max_elements) {\n            max_elements_ = max_elements;\n\n            has_deletions_ = false;\n            num_deleted_ = 0;\n            data_size_ = s->get_data_size();\n            fstdistfunc_ = s->get_dist_func();\n            dist_func_param_ = s->get_dist_func_param();\n            M_ = M;\n            maxM_ = M_;\n            maxM0_ = M_ * 2;\n            ef_construction_ = std::max(ef_construction,M_);\n            ef_ = 10;\n\n            level_generator_.seed(random_seed);\n            update_probability_generator_.seed(random_seed + 1);\n\n            size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint);\n            size_data_per_element_ = size_links_level0_ + data_size_ + sizeof(labeltype);\n            offsetData_ = size_links_level0_;\n            label_offset_ = size_links_level0_ + data_size_;\n            offsetLevel0_ = 0;\n\n            data_level0_memory_ = (char *) malloc(max_elements_ * size_data_per_element_);\n            if (data_level0_memory_ == nullptr)\n                throw std::runtime_error(\"Not enough memory\");\n\n            cur_element_count = 0;\n\n            visited_list_pool_ = new VisitedListPool(1, max_elements);\n\n            //initializations for special treatment of the first node\n            enterpoint_node_ = -1;\n            maxlevel_ = -1;\n\n            linkLists_ = (char **) malloc(sizeof(void *) * max_elements_);\n            if (linkLists_ == nullptr)\n                throw std::runtime_error(\"Not enough memory: HierarchicalNSW failed to allocate linklists\");\n            size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint);\n            mult_ = 1 / log(1.0 * M_);\n            revSize_ = 1.0 / mult_;\n        }\n\n        struct CompareByFirst {\n            constexpr bool operator()(std::pair<dist_t, tableint> const &a,\n                                      std::pair<dist_t, tableint> const &b) const noexcept {\n                return a.first < b.first;\n            }\n        };\n\n        ~HierarchicalNSW() {\n\n            free(data_level0_memory_);\n            for (tableint i = 0; i < cur_element_count; i++) {\n                if (element_levels_[i] > 0)\n                    free(linkLists_[i]);\n            }\n            free(linkLists_);\n            delete visited_list_pool_;\n        }\n\n        size_t max_elements_;\n        size_t cur_element_count;\n        size_t size_data_per_element_;\n        size_t size_links_per_element_;\n        size_t num_deleted_;\n\n        size_t M_;\n        size_t maxM_;\n        size_t maxM0_;\n        size_t ef_construction_;\n\n        double mult_, revSize_;\n        int maxlevel_;\n\n\n        VisitedListPool *visited_list_pool_;\n        std::mutex cur_element_count_guard_;\n\n        std::vector<std::mutex> link_list_locks_;\n\n        // Locks to prevent race condition during update/insert of an element at same time.\n        // Note: Locks for additions can also be used to prevent this race condition if the querying of KNN is not exposed along with update/inserts i.e multithread insert/update/query in parallel.\n        std::vector<std::mutex> link_list_update_locks_;\n        tableint enterpoint_node_;\n\n        size_t size_links_level0_;\n        size_t offsetData_, offsetLevel0_;\n\n        char *data_level0_memory_;\n        char **linkLists_;\n        std::vector<int> element_levels_;\n\n        size_t data_size_;\n\n        bool has_deletions_;\n\n        size_t label_offset_;\n        DISTFUNC<dist_t> fstdistfunc_;\n        void *dist_func_param_;\n        std::unordered_map<labeltype, tableint> label_lookup_;\n\n        std::default_random_engine level_generator_;\n        std::default_random_engine update_probability_generator_;\n\n        inline labeltype getExternalLabel(tableint internal_id) const {\n            labeltype return_label;\n            memcpy(&return_label,(data_level0_memory_ + internal_id * size_data_per_element_ + label_offset_), sizeof(labeltype));\n            return return_label;\n        }\n\n        inline void setExternalLabel(tableint internal_id, labeltype label) const {\n            memcpy((data_level0_memory_ + internal_id * size_data_per_element_ + label_offset_), &label, sizeof(labeltype));\n        }\n\n        inline labeltype *getExternalLabeLp(tableint internal_id) const {\n            return (labeltype *) (data_level0_memory_ + internal_id * size_data_per_element_ + label_offset_);\n        }\n\n        inline char *getDataByInternalId(tableint internal_id) const {\n            return (data_level0_memory_ + internal_id * size_data_per_element_ + offsetData_);\n        }\n\n        int getRandomLevel(double reverse_size) {\n            std::uniform_real_distribution<double> distribution(0.0, 1.0);\n            double r = -log(distribution(level_generator_)) * reverse_size;\n            return (int) r;\n        }\n\n        // TODO:\n        std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst>\n        searchBaseLayer(tableint ep_id, const void *data_point, int layer, const local_state_t *local_state_ptr) {\n            VisitedList *vl = visited_list_pool_->getFreeVisitedList();\n            vl_type *visited_array = vl->mass;\n            vl_type visited_array_tag = vl->curV;\n\n            std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> top_candidates;\n            std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> candidateSet;\n\n            dist_t lowerBound;\n            if (!isMarkedDeleted(ep_id)) {\n      dist_t dist = fstdistfunc_(data_point, getDataByInternalId(ep_id), dist_func_param_, local_state_ptr);\n                top_candidates.emplace(dist, ep_id);\n                lowerBound = dist;\n                candidateSet.emplace(-dist, ep_id);\n            } else {\n                lowerBound = std::numeric_limits<dist_t>::max();\n                candidateSet.emplace(-lowerBound, ep_id);\n            }\n            visited_array[ep_id] = visited_array_tag;\n\n            while (!candidateSet.empty()) {\n                std::pair<dist_t, tableint> curr_el_pair = candidateSet.top();\n                if ((-curr_el_pair.first) > lowerBound) {\n                    break;\n                }\n                candidateSet.pop();\n\n                tableint curNodeNum = curr_el_pair.second;\n\n                std::unique_lock <std::mutex> lock(link_list_locks_[curNodeNum]);\n\n                int *data;// = (int *)(linkList0_ + curNodeNum * size_links_per_element0_);\n                if (layer == 0) {\n                    data = (int*)get_linklist0(curNodeNum);\n                } else {\n                    data = (int*)get_linklist(curNodeNum, layer);\n//                    data = (int *) (linkLists_[curNodeNum] + (layer - 1) * size_links_per_element_);\n                }\n                size_t size = getListCount((linklistsizeint*)data);\n                tableint *datal = (tableint *) (data + 1);\n#ifdef USE_SSE\n                _mm_prefetch((char *) (visited_array + *(data + 1)), _MM_HINT_T0);\n                _mm_prefetch((char *) (visited_array + *(data + 1) + 64), _MM_HINT_T0);\n                _mm_prefetch(getDataByInternalId(*datal), _MM_HINT_T0);\n                _mm_prefetch(getDataByInternalId(*(datal + 1)), _MM_HINT_T0);\n#endif\n\n                for (size_t j = 0; j < size; j++) {\n                    tableint candidate_id = *(datal + j);\n//                    if (candidate_id == 0) continue;\n#ifdef USE_SSE\n                    _mm_prefetch((char *) (visited_array + *(datal + j + 1)), _MM_HINT_T0);\n                    _mm_prefetch(getDataByInternalId(*(datal + j + 1)), _MM_HINT_T0);\n#endif\n                    if (visited_array[candidate_id] == visited_array_tag) continue;\n                    visited_array[candidate_id] = visited_array_tag;\n                    char *currObj1 = (getDataByInternalId(candidate_id));\n\n                    dist_t dist1 = fstdistfunc_(data_point, currObj1, dist_func_param_, local_state_ptr);\n                    if (top_candidates.size() < ef_construction_ || lowerBound > dist1) {\n                        candidateSet.emplace(-dist1, candidate_id);\n#ifdef USE_SSE\n                        _mm_prefetch(getDataByInternalId(candidateSet.top().second), _MM_HINT_T0);\n#endif\n\n                        if (!isMarkedDeleted(candidate_id))\n                            top_candidates.emplace(dist1, candidate_id);\n\n                        if (top_candidates.size() > ef_construction_)\n                            top_candidates.pop();\n\n                        if (!top_candidates.empty())\n                            lowerBound = top_candidates.top().first;\n                    }\n                }\n            }\n            visited_list_pool_->releaseVisitedList(vl);\n\n            return top_candidates;\n        }\n\n        mutable std::atomic<long> metric_distance_computations;\n        mutable std::atomic<long> metric_hops;\n\n        template <bool has_deletions, bool collect_metrics=false >\n        std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst>\n        searchBaseLayerST(tableint ep_id, const void *data_point, size_t ef, const local_state_t *local_state_ptr) const {\n            VisitedList *vl = visited_list_pool_->getFreeVisitedList();\n            vl_type *visited_array = vl->mass;\n            vl_type visited_array_tag = vl->curV;\n\n            std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> top_candidates;\n            std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> candidate_set;\n\n            dist_t lowerBound;\n            if (!has_deletions || !isMarkedDeleted(ep_id)) {\n                dist_t dist = fstdistfunc_(data_point, getDataByInternalId(ep_id), dist_func_param_, local_state_ptr);\n                lowerBound = dist;\n                top_candidates.emplace(dist, ep_id);\n                candidate_set.emplace(-dist, ep_id);\n            } else {\n                lowerBound = std::numeric_limits<dist_t>::max();\n                candidate_set.emplace(-lowerBound, ep_id);\n            }\n\n            visited_array[ep_id] = visited_array_tag;\n\n            while (!candidate_set.empty()) {\n\n                std::pair<dist_t, tableint> current_node_pair = candidate_set.top();\n\n                if ((-current_node_pair.first) > lowerBound && (top_candidates.size() == ef || has_deletions == false)) {\n                    break;\n                }\n                candidate_set.pop();\n\n                tableint current_node_id = current_node_pair.second;\n                int *data = (int *) get_linklist0(current_node_id);\n                size_t size = getListCount((linklistsizeint*)data);\n//                bool cur_node_deleted = isMarkedDeleted(current_node_id);\n                if(collect_metrics){\n                    metric_hops++;\n                    metric_distance_computations+=size;\n                }\n\n#ifdef USE_SSE\n                _mm_prefetch((char *) (visited_array + *(data + 1)), _MM_HINT_T0);\n                _mm_prefetch((char *) (visited_array + *(data + 1) + 64), _MM_HINT_T0);\n                _mm_prefetch(data_level0_memory_ + (*(data + 1)) * size_data_per_element_ + offsetData_, _MM_HINT_T0);\n                _mm_prefetch((char *) (data + 2), _MM_HINT_T0);\n#endif\n\n                for (size_t j = 1; j <= size; j++) {\n                    int candidate_id = *(data + j);\n//                    if (candidate_id == 0) continue;\n#ifdef USE_SSE\n                    _mm_prefetch((char *) (visited_array + *(data + j + 1)), _MM_HINT_T0);\n                    _mm_prefetch(data_level0_memory_ + (*(data + j + 1)) * size_data_per_element_ + offsetData_,\n                                 _MM_HINT_T0);////////////\n#endif\n                    if (!(visited_array[candidate_id] == visited_array_tag)) {\n\n                        visited_array[candidate_id] = visited_array_tag;\n\n                        char *currObj1 = (getDataByInternalId(candidate_id));\n                        dist_t dist = fstdistfunc_(data_point, currObj1, dist_func_param_, local_state_ptr);\n\n                        if (top_candidates.size() < ef || lowerBound > dist) {\n                            candidate_set.emplace(-dist, candidate_id);\n#ifdef USE_SSE\n                            _mm_prefetch(data_level0_memory_ + candidate_set.top().second * size_data_per_element_ +\n                                         offsetLevel0_,///////////\n                                         _MM_HINT_T0);////////////////////////\n#endif\n\n                            if (!has_deletions || !isMarkedDeleted(candidate_id))\n                                top_candidates.emplace(dist, candidate_id);\n\n                            if (top_candidates.size() > ef)\n                                top_candidates.pop();\n\n                            if (!top_candidates.empty())\n                                lowerBound = top_candidates.top().first;\n                        }\n                    }\n                }\n            }\n\n            visited_list_pool_->releaseVisitedList(vl);\n            return top_candidates;\n        }\n\n  // TODO\n  template <bool has_deletions, bool collect_metrics = false>\n  std::priority_queue<std::pair<dist_t, tableint>,\n                      std::vector<std::pair<dist_t, tableint>>, CompareByFirst>\n  searchBaseLayerSTWithFilter(tableint ep_id, const void *data_point,\n                              const binary_fuse16_t *filter, size_t ef,\n                              const local_state_t *local_state_ptr) const {\n    VisitedList *vl = visited_list_pool_->getFreeVisitedList();\n    vl_type *visited_array = vl->mass;\n    vl_type visited_array_tag = vl->curV;\n\n    std::priority_queue<std::pair<dist_t, tableint>,\n                        std::vector<std::pair<dist_t, tableint>>,\n                        CompareByFirst>\n        top_candidates;\n    std::priority_queue<std::pair<dist_t, tableint>,\n                        std::vector<std::pair<dist_t, tableint>>,\n                        CompareByFirst>\n        candidate_set;\n\n    dist_t lowerBound;\n\n    uint64_t label = getExternalLabel(ep_id);\n    if (binary_fuse16_contain(label, filter)) {\n      dist_t dist = fstdistfunc_(data_point, getDataByInternalId(ep_id),\n                                 dist_func_param_, local_state_ptr);\n      lowerBound = dist;\n      top_candidates.emplace(dist, ep_id);\n      candidate_set.emplace(-dist, ep_id);\n    } else {\n      lowerBound = std::numeric_limits<dist_t>::max();\n      candidate_set.emplace(-lowerBound, ep_id);\n    }\n\n    visited_array[ep_id] = visited_array_tag;\n\n    while (!candidate_set.empty()) {\n\n      std::pair<dist_t, tableint> current_node_pair = candidate_set.top();\n\n      if ((-current_node_pair.first) > lowerBound) {\n        break;\n      }\n      candidate_set.pop();\n\n      tableint current_node_id = current_node_pair.second;\n      int *data = (int *)get_linklist0(current_node_id);\n      size_t size = getListCount((linklistsizeint *)data);\n      //                bool cur_node_deleted =\n      //                isMarkedDeleted(current_node_id);\n      if (collect_metrics) {\n        metric_hops++;\n        metric_distance_computations += size;\n      }\n\n#ifdef USE_SSE\n      _mm_prefetch((char *)(visited_array + *(data + 1)), _MM_HINT_T0);\n      _mm_prefetch((char *)(visited_array + *(data + 1) + 64), _MM_HINT_T0);\n      _mm_prefetch(data_level0_memory_ +\n                       (*(data + 1)) * size_data_per_element_ + offsetData_,\n                   _MM_HINT_T0);\n      _mm_prefetch((char *)(data + 2), _MM_HINT_T0);\n#endif\n\n      for (size_t j = 1; j <= size; j++) {\n        int candidate_id = *(data + j);\n//                    if (candidate_id == 0) continue;\n#ifdef USE_SSE\n        _mm_prefetch((char *)(visited_array + *(data + j + 1)), _MM_HINT_T0);\n        _mm_prefetch(data_level0_memory_ +\n                         (*(data + j + 1)) * size_data_per_element_ +\n                         offsetData_,\n                     _MM_HINT_T0); ////////////\n#endif\n        if (!(visited_array[candidate_id] == visited_array_tag)) {\n\n          visited_array[candidate_id] = visited_array_tag;\n\n          char *currObj1 = (getDataByInternalId(candidate_id));\n          dist_t dist = fstdistfunc_(data_point, currObj1, dist_func_param_,\n                                     local_state_ptr);\n\n          if (top_candidates.size() < ef || lowerBound > dist) {\n            candidate_set.emplace(-dist, candidate_id);\n#ifdef USE_SSE\n            _mm_prefetch(data_level0_memory_ +\n                             candidate_set.top().second *\n                                 size_data_per_element_ +\n                             offsetLevel0_, ///////////\n                         _MM_HINT_T0);      ////////////////////////\n#endif\n\n            uint64_t et_label = getExternalLabel(candidate_id);\n\n            if (binary_fuse16_contain(et_label, filter))\n              top_candidates.emplace(dist, candidate_id);\n\n            if (top_candidates.size() > ef)\n              top_candidates.pop();\n\n            if (!top_candidates.empty())\n              lowerBound = top_candidates.top().first;\n          }\n        }\n      }\n    }\n\n    visited_list_pool_->releaseVisitedList(vl);\n    return top_candidates;\n  }\n\n        // TODO\n        void getNeighborsByHeuristic2(\n                std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> &top_candidates,\n        const size_t M, const local_state_t *local_state_ptr) {\n            if (top_candidates.size() < M) {\n                return;\n            }\n\n            std::priority_queue<std::pair<dist_t, tableint>> queue_closest;\n            std::vector<std::pair<dist_t, tableint>> return_list;\n            while (top_candidates.size() > 0) {\n                queue_closest.emplace(-top_candidates.top().first, top_candidates.top().second);\n                top_candidates.pop();\n            }\n\n            while (queue_closest.size()) {\n                if (return_list.size() >= M)\n                    break;\n                std::pair<dist_t, tableint> curent_pair = queue_closest.top();\n                dist_t dist_to_query = -curent_pair.first;\n                queue_closest.pop();\n                bool good = true;\n\n                for (std::pair<dist_t, tableint> second_pair : return_list) {\n                    dist_t curdist =\n                            fstdistfunc_(getDataByInternalId(second_pair.second),\n                                         getDataByInternalId(curent_pair.second),\n                                         dist_func_param_, local_state_ptr);;\n                    if (curdist < dist_to_query) {\n                        good = false;\n                        break;\n                    }\n                }\n                if (good) {\n                    return_list.push_back(curent_pair);\n                }\n            }\n\n            for (std::pair<dist_t, tableint> curent_pair : return_list) {\n                top_candidates.emplace(-curent_pair.first, curent_pair.second);\n            }\n        }\n\n\n        linklistsizeint *get_linklist0(tableint internal_id) const {\n            return (linklistsizeint *) (data_level0_memory_ + internal_id * size_data_per_element_ + offsetLevel0_);\n        };\n\n        linklistsizeint *get_linklist0(tableint internal_id, char *data_level0_memory_) const {\n            return (linklistsizeint *) (data_level0_memory_ + internal_id * size_data_per_element_ + offsetLevel0_);\n        };\n\n        linklistsizeint *get_linklist(tableint internal_id, int level) const {\n            return (linklistsizeint *) (linkLists_[internal_id] + (level - 1) * size_links_per_element_);\n        };\n\n        linklistsizeint *get_linklist_at_level(tableint internal_id, int level) const {\n            return level == 0 ? get_linklist0(internal_id) : get_linklist(internal_id, level);\n        };\n\n        tableint mutuallyConnectNewElement(const void *data_point, tableint cur_c,\n                                       std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> &top_candidates,\n        int level, bool isUpdate, const local_state_t *local_state_ptr) {\n            size_t Mcurmax = level ? maxM_ : maxM0_;\n            getNeighborsByHeuristic2(top_candidates, M_, local_state_ptr);\n            if (top_candidates.size() > M_)\n                throw std::runtime_error(\"Should be not be more than M_ candidates returned by the heuristic\");\n\n            std::vector<tableint> selectedNeighbors;\n            selectedNeighbors.reserve(M_);\n            while (top_candidates.size() > 0) {\n                selectedNeighbors.push_back(top_candidates.top().second);\n                top_candidates.pop();\n            }\n\n            tableint next_closest_entry_point = selectedNeighbors.back();\n\n            {\n                linklistsizeint *ll_cur;\n                if (level == 0)\n                    ll_cur = get_linklist0(cur_c);\n                else\n                    ll_cur = get_linklist(cur_c, level);\n\n                if (*ll_cur && !isUpdate) {\n                    throw std::runtime_error(\"The newly inserted element should have blank link list\");\n                }\n                setListCount(ll_cur,selectedNeighbors.size());\n                tableint *data = (tableint *) (ll_cur + 1);\n                for (size_t idx = 0; idx < selectedNeighbors.size(); idx++) {\n                    if (data[idx] && !isUpdate)\n                        throw std::runtime_error(\"Possible memory corruption\");\n                    if (level > element_levels_[selectedNeighbors[idx]])\n                        throw std::runtime_error(\"Trying to make a link on a non-existent level\");\n\n                    data[idx] = selectedNeighbors[idx];\n\n                }\n            }\n\n            for (size_t idx = 0; idx < selectedNeighbors.size(); idx++) {\n\n                std::unique_lock <std::mutex> lock(link_list_locks_[selectedNeighbors[idx]]);\n\n                linklistsizeint *ll_other;\n                if (level == 0)\n                    ll_other = get_linklist0(selectedNeighbors[idx]);\n                else\n                    ll_other = get_linklist(selectedNeighbors[idx], level);\n\n                size_t sz_link_list_other = getListCount(ll_other);\n\n                if (sz_link_list_other > Mcurmax)\n                    throw std::runtime_error(\"Bad value of sz_link_list_other\");\n                if (selectedNeighbors[idx] == cur_c)\n                    throw std::runtime_error(\"Trying to connect an element to itself\");\n                if (level > element_levels_[selectedNeighbors[idx]])\n                    throw std::runtime_error(\"Trying to make a link on a non-existent level\");\n\n                tableint *data = (tableint *) (ll_other + 1);\n\n                bool is_cur_c_present = false;\n                if (isUpdate) {\n                    for (size_t j = 0; j < sz_link_list_other; j++) {\n                        if (data[j] == cur_c) {\n                            is_cur_c_present = true;\n                            break;\n                        }\n                    }\n                }\n\n                // If cur_c is already present in the neighboring connections of `selectedNeighbors[idx]` then no need to modify any connections or run the heuristics.\n                if (!is_cur_c_present) {\n                    if (sz_link_list_other < Mcurmax) {\n                        data[sz_link_list_other] = cur_c;\n                        setListCount(ll_other, sz_link_list_other + 1);\n                    } else {\n                        // finding the \"weakest\" element to replace it with the new one\n                        dist_t d_max = fstdistfunc_(getDataByInternalId(cur_c), getDataByInternalId(selectedNeighbors[idx]),\n                                                    dist_func_param_, local_state_ptr);\n                        // Heuristic:\n                        std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> candidates;\n                        candidates.emplace(d_max, cur_c);\n\n                        for (size_t j = 0; j < sz_link_list_other; j++) {\n                            candidates.emplace(\n                                fstdistfunc_(getDataByInternalId(data[j]), getDataByInternalId(selectedNeighbors[idx]),\n                                             dist_func_param_, local_state_ptr), data[j]);\n                        }\n\n                        getNeighborsByHeuristic2(candidates, Mcurmax, local_state_ptr);\n\n                        int indx = 0;\n                        while (candidates.size() > 0) {\n                            data[indx] = candidates.top().second;\n                            candidates.pop();\n                            indx++;\n                        }\n\n                        setListCount(ll_other, indx);\n                        // Nearest K:\n                        /*int indx = -1;\n                        for (int j = 0; j < sz_link_list_other; j++) {\n                            dist_t d = fstdistfunc_(getDataByInternalId(data[j]), getDataByInternalId(rez[idx]), dist_func_param_, nullptr);\n                            if (d > d_max) {\n                                indx = j;\n                                d_max = d;\n                            }\n                        }\n                        if (indx >= 0) {\n                            data[indx] = cur_c;\n                        } */\n                    }\n                }\n            }\n\n            return next_closest_entry_point;\n        }\n\n        std::mutex global;\n        size_t ef_;\n\n        void setEf(size_t ef) {\n            ef_ = ef;\n        }\n\n\n        std::priority_queue<std::pair<dist_t, tableint>> searchKnnInternal(void *query_data, int k) {\n            std::priority_queue<std::pair<dist_t, tableint  >> top_candidates;\n            if (cur_element_count == 0) return top_candidates;\n\n            // FIXME: Never used, so fix index to 0.\n            local_state_t local_state;\n            local_state.batch_index = 0;\n\n            tableint currObj = enterpoint_node_;\n            dist_t curdist = fstdistfunc_(query_data, getDataByInternalId(enterpoint_node_), dist_func_param_, &local_state);\n\n            for (size_t level = maxlevel_; level > 0; level--) {\n                bool changed = true;\n                while (changed) {\n                    changed = false;\n                    int *data;\n                    data = (int *) get_linklist(currObj,level);\n                    int size = getListCount(data);\n                    tableint *datal = (tableint *) (data + 1);\n                    for (int i = 0; i < size; i++) {\n                        tableint cand = datal[i];\n                        if (cand < 0 || cand > max_elements_)\n                            throw std::runtime_error(\"cand error\");\n                        dist_t d = fstdistfunc_(query_data, getDataByInternalId(cand), dist_func_param_, nullptr);\n\n                        if (d < curdist) {\n                            curdist = d;\n                            currObj = cand;\n                            changed = true;\n                        }\n                    }\n                }\n            }\n\n            if (num_deleted_) {\n                std::priority_queue<std::pair<dist_t, tableint  >> top_candidates1=searchBaseLayerST<true>(currObj, query_data,\n                                                                                                           ef_, &local_state);\n                top_candidates.swap(top_candidates1);\n            }\n            else{\n                std::priority_queue<std::pair<dist_t, tableint  >> top_candidates1=searchBaseLayerST<false>(currObj, query_data,\n                                                                                                            ef_, &local_state);\n                top_candidates.swap(top_candidates1);\n            }\n\n            while (top_candidates.size() > k) {\n                top_candidates.pop();\n            }\n            return top_candidates;\n        };\n\n        void resizeIndex(size_t new_max_elements){\n            if (new_max_elements<cur_element_count)\n                throw std::runtime_error(\"Cannot resize, max element is less than the current number of elements\");\n\n\n            delete visited_list_pool_;\n            visited_list_pool_ = new VisitedListPool(1, new_max_elements);\n\n\n            element_levels_.resize(new_max_elements);\n\n            std::vector<std::mutex>(new_max_elements).swap(link_list_locks_);\n\n            // Reallocate base layer\n            char * data_level0_memory_new = (char *) realloc(data_level0_memory_, new_max_elements * size_data_per_element_);\n            if (data_level0_memory_new == nullptr)\n                throw std::runtime_error(\"Not enough memory: resizeIndex failed to allocate base layer\");\n            data_level0_memory_ = data_level0_memory_new;\n\n            // Reallocate all other layers\n            char ** linkLists_new = (char **) realloc(linkLists_, sizeof(void *) * new_max_elements);\n            if (linkLists_new == nullptr)\n                throw std::runtime_error(\"Not enough memory: resizeIndex failed to allocate other layers\");\n            linkLists_ = linkLists_new;\n\n            max_elements_ = new_max_elements;\n        }\n\n        void saveIndex(const std::string &location) {\n            std::ofstream output(location, std::ios::binary);\n            std::streampos position;\n\n            writeBinaryPOD(output, offsetLevel0_);\n            writeBinaryPOD(output, max_elements_);\n            writeBinaryPOD(output, cur_element_count);\n            writeBinaryPOD(output, size_data_per_element_);\n            writeBinaryPOD(output, label_offset_);\n            writeBinaryPOD(output, offsetData_);\n            writeBinaryPOD(output, maxlevel_);\n            writeBinaryPOD(output, enterpoint_node_);\n            writeBinaryPOD(output, maxM_);\n\n            writeBinaryPOD(output, maxM0_);\n            writeBinaryPOD(output, M_);\n            writeBinaryPOD(output, mult_);\n            writeBinaryPOD(output, ef_construction_);\n\n            output.write(data_level0_memory_, cur_element_count * size_data_per_element_);\n\n            for (size_t i = 0; i < cur_element_count; i++) {\n                unsigned int linkListSize = element_levels_[i] > 0 ? size_links_per_element_ * element_levels_[i] : 0;\n                writeBinaryPOD(output, linkListSize);\n                if (linkListSize)\n                    output.write(linkLists_[i], linkListSize);\n            }\n            output.close();\n        }\n\n        void loadIndex(const std::string &location, SpaceInterface<dist_t> *s, size_t max_elements_i=0) {\n            std::ifstream input(location, std::ios::binary);\n\n            if (!input.is_open())\n                throw std::runtime_error(\"Cannot open file\");\n\n            // get file size:\n            input.seekg(0,input.end);\n            std::streampos total_filesize=input.tellg();\n            input.seekg(0,input.beg);\n\n            readBinaryPOD(input, offsetLevel0_);\n            readBinaryPOD(input, max_elements_);\n            readBinaryPOD(input, cur_element_count);\n\n            size_t max_elements = max_elements_i;\n            if(max_elements < cur_element_count)\n                max_elements = max_elements_;\n            max_elements_ = max_elements;\n            readBinaryPOD(input, size_data_per_element_);\n            readBinaryPOD(input, label_offset_);\n            readBinaryPOD(input, offsetData_);\n            readBinaryPOD(input, maxlevel_);\n            readBinaryPOD(input, enterpoint_node_);\n\n            readBinaryPOD(input, maxM_);\n            readBinaryPOD(input, maxM0_);\n            readBinaryPOD(input, M_);\n            readBinaryPOD(input, mult_);\n            readBinaryPOD(input, ef_construction_);\n\n\n            data_size_ = s->get_data_size();\n            fstdistfunc_ = s->get_dist_func();\n            dist_func_param_ = s->get_dist_func_param();\n\n            auto pos=input.tellg();\n\n\n            /// Optional - check if index is ok:\n\n            input.seekg(cur_element_count * size_data_per_element_,input.cur);\n            for (size_t i = 0; i < cur_element_count; i++) {\n                if(input.tellg() < 0 || input.tellg()>=total_filesize){\n                    throw std::runtime_error(\"Index seems to be corrupted or unsupported\");\n                }\n\n                unsigned int linkListSize;\n                readBinaryPOD(input, linkListSize);\n                if (linkListSize != 0) {\n                    input.seekg(linkListSize,input.cur);\n                }\n            }\n\n            // throw exception if it either corrupted or old index\n            if(input.tellg()!=total_filesize)\n                throw std::runtime_error(\"Index seems to be corrupted or unsupported\");\n\n            input.clear();\n\n            /// Optional check end\n\n            input.seekg(pos,input.beg);\n\n            data_level0_memory_ = (char *) malloc(max_elements * size_data_per_element_);\n            if (data_level0_memory_ == nullptr)\n                throw std::runtime_error(\"Not enough memory: loadIndex failed to allocate level0\");\n            input.read(data_level0_memory_, cur_element_count * size_data_per_element_);\n\n            size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint);\n\n            size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint);\n            std::vector<std::mutex>(max_elements).swap(link_list_locks_);\n            std::vector<std::mutex>(max_update_element_locks).swap(link_list_update_locks_);\n\n            visited_list_pool_ = new VisitedListPool(1, max_elements);\n\n            linkLists_ = (char **) malloc(sizeof(void *) * max_elements);\n            if (linkLists_ == nullptr)\n                throw std::runtime_error(\"Not enough memory: loadIndex failed to allocate linklists\");\n            element_levels_ = std::vector<int>(max_elements);\n            revSize_ = 1.0 / mult_;\n            ef_ = 10;\n            for (size_t i = 0; i < cur_element_count; i++) {\n                label_lookup_[getExternalLabel(i)]=i;\n                unsigned int linkListSize;\n                readBinaryPOD(input, linkListSize);\n                if (linkListSize == 0) {\n                    element_levels_[i] = 0;\n\n                    linkLists_[i] = nullptr;\n                } else {\n                    element_levels_[i] = linkListSize / size_links_per_element_;\n                    linkLists_[i] = (char *) malloc(linkListSize);\n                    if (linkLists_[i] == nullptr)\n                        throw std::runtime_error(\"Not enough memory: loadIndex failed to allocate linklist\");\n                    input.read(linkLists_[i], linkListSize);\n                }\n            }\n\n            for (size_t i = 0; i < cur_element_count; i++) {\n                if(isMarkedDeleted(i))\n                    num_deleted_ += 1;\n            }\n\n            input.close();\n\n            return;\n        }\n\n        template<typename data_t>\n        std::vector<data_t> getDataByLabel(labeltype label) const\n        {\n            tableint label_c;\n            auto search = label_lookup_.find(label);\n            if (search == label_lookup_.end() || isMarkedDeleted(search->second)) {\n                throw std::runtime_error(\"Label not found\");\n            }\n            label_c = search->second;\n\n            char* data_ptrv = getDataByInternalId(label_c);\n            size_t dim = *((size_t *) dist_func_param_);\n            std::vector<data_t> data;\n            data_t* data_ptr = (data_t*) data_ptrv;\n            for (int i = 0; i < dim; i++) {\n                data.push_back(*data_ptr);\n                data_ptr += 1;\n            }\n            return data;\n        }\n\n        static const unsigned char DELETE_MARK = 0x01;\n        // static const unsigned char REUSE_MARK = 0x10;\n        /**\n         * Marks an element with the given label deleted, does NOT really change the current graph.\n         * @param label\n         */\n        void markDelete(labeltype label)\n        {\n            auto search = label_lookup_.find(label);\n            if (search == label_lookup_.end()) {\n                throw std::runtime_error(\"Label not found\");\n            }\n            tableint internalId = search->second;\n            markDeletedInternal(internalId);\n        }\n\n        /**\n         * Uses the first 8 bits of the memory for the linked list to store the mark,\n         * whereas maxM0_ has to be limited to the lower 24 bits, however, still large enough in almost all cases.\n         * @param internalId\n         */\n        void markDeletedInternal(tableint internalId) {\n            assert(internalId < cur_element_count);\n            if (!isMarkedDeleted(internalId))\n            {\n                unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;\n                *ll_cur |= DELETE_MARK;\n                num_deleted_ += 1;\n            }\n            else\n            {\n                throw std::runtime_error(\"The requested to delete element is already deleted\");\n            }\n        }\n\n        /**\n         * Remove the deleted mark of the node, does NOT really change the current graph.\n         * @param label\n         */\n        void unmarkDelete(labeltype label)\n        {\n            auto search = label_lookup_.find(label);\n            if (search == label_lookup_.end()) {\n                throw std::runtime_error(\"Label not found\");\n            }\n            tableint internalId = search->second;\n            unmarkDeletedInternal(internalId);\n        }\n\n        /**\n         * Remove the deleted mark of the node.\n         * @param internalId\n         */\n        void unmarkDeletedInternal(tableint internalId) {\n            assert(internalId < cur_element_count);\n            if (isMarkedDeleted(internalId))\n            {\n                unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;\n                *ll_cur &= ~DELETE_MARK;\n                num_deleted_ -= 1;\n            }\n            else\n            {\n                throw std::runtime_error(\"The requested to undelete element is not deleted\");\n            }\n        }\n\n        /**\n         * Checks the first 8 bits of the memory to see if the element is marked deleted.\n         * @param internalId\n         * @return\n         */\n        bool isMarkedDeleted(tableint internalId) const {\n            unsigned char *ll_cur = ((unsigned char*)get_linklist0(internalId))+2;\n            return *ll_cur & DELETE_MARK;\n        }\n\n        unsigned short int getListCount(linklistsizeint * ptr) const {\n            return *((unsigned short int *)ptr);\n        }\n\n        void setListCount(linklistsizeint * ptr, unsigned short int size) const {\n            *((unsigned short int*)(ptr))=*((unsigned short int *)&size);\n        }\n\n        void addPoint(const void *data_point, labeltype label, size_t batch_index) {\n            addPoint(data_point, label, -1, batch_index);\n        }\n\n        void updatePoint(const void *dataPoint, tableint internalId, float updateNeighborProbability, const local_state_t *local_state_ptr) {\n            // update the feature vector associated with existing point with new vector\n            memcpy(getDataByInternalId(internalId), dataPoint, data_size_);\n\n            int maxLevelCopy = maxlevel_;\n            tableint entryPointCopy = enterpoint_node_;\n            // If point to be updated is entry point and graph just contains single element then just return.\n            if (entryPointCopy == internalId && cur_element_count == 1)\n                return;\n\n            int elemLevel = element_levels_[internalId];\n            std::uniform_real_distribution<float> distribution(0.0, 1.0);\n            for (int layer = 0; layer <= elemLevel; layer++) {\n                std::unordered_set<tableint> sCand;\n                std::unordered_set<tableint> sNeigh;\n                std::vector<tableint> listOneHop = getConnectionsWithLock(internalId, layer);\n                if (listOneHop.size() == 0)\n                    continue;\n\n                sCand.insert(internalId);\n\n                for (auto&& elOneHop : listOneHop) {\n                    sCand.insert(elOneHop);\n\n                    if (distribution(update_probability_generator_) > updateNeighborProbability)\n                        continue;\n\n                    sNeigh.insert(elOneHop);\n\n                    std::vector<tableint> listTwoHop = getConnectionsWithLock(elOneHop, layer);\n                    for (auto&& elTwoHop : listTwoHop) {\n                        sCand.insert(elTwoHop);\n                    }\n                }\n\n                for (auto&& neigh : sNeigh) {\n                    // if (neigh == internalId)\n                    //     continue;\n\n                    std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> candidates;\n                    size_t size = sCand.find(neigh) == sCand.end() ? sCand.size() : sCand.size() - 1; // sCand guaranteed to have size >= 1\n                    size_t elementsToKeep = std::min(ef_construction_, size);\n                    for (auto&& cand : sCand) {\n                        if (cand == neigh)\n                            continue;\n\n                        dist_t distance = fstdistfunc_(getDataByInternalId(neigh), getDataByInternalId(cand), dist_func_param_, local_state_ptr);\n                        if (candidates.size() < elementsToKeep) {\n                            candidates.emplace(distance, cand);\n                        } else {\n                            if (distance < candidates.top().first) {\n                                candidates.pop();\n                                candidates.emplace(distance, cand);\n                            }\n                        }\n                    }\n\n                    // Retrieve neighbours using heuristic and set connections.\n                    getNeighborsByHeuristic2(candidates, layer == 0 ? maxM0_ : maxM_, local_state_ptr);\n\n                    {\n                        std::unique_lock <std::mutex> lock(link_list_locks_[neigh]);\n                        linklistsizeint *ll_cur;\n                        ll_cur = get_linklist_at_level(neigh, layer);\n                        size_t candSize = candidates.size();\n                        setListCount(ll_cur, candSize);\n                        tableint *data = (tableint *) (ll_cur + 1);\n                        for (size_t idx = 0; idx < candSize; idx++) {\n                            data[idx] = candidates.top().second;\n                            candidates.pop();\n                        }\n                    }\n                }\n            }\n\n            repairConnectionsForUpdate(dataPoint, entryPointCopy, internalId, elemLevel, maxLevelCopy, local_state_ptr);\n        };\n\n        void repairConnectionsForUpdate(const void *dataPoint, tableint entryPointInternalId, tableint dataPointInternalId, int dataPointLevel, int maxLevel, const local_state_t *local_state_ptr) {\n            tableint currObj = entryPointInternalId;\n            if (dataPointLevel < maxLevel) {\n                dist_t curdist = fstdistfunc_(dataPoint, getDataByInternalId(currObj), dist_func_param_, local_state_ptr);\n                for (int level = maxLevel; level > dataPointLevel; level--) {\n                    bool changed = true;\n                    while (changed) {\n                        changed = false;\n                        unsigned int *data;\n                        std::unique_lock <std::mutex> lock(link_list_locks_[currObj]);\n                        data = get_linklist_at_level(currObj,level);\n                        int size = getListCount(data);\n                        tableint *datal = (tableint *) (data + 1);\n#ifdef USE_SSE\n                        _mm_prefetch(getDataByInternalId(*datal), _MM_HINT_T0);\n#endif\n                        for (int i = 0; i < size; i++) {\n#ifdef USE_SSE\n                            _mm_prefetch(getDataByInternalId(*(datal + i + 1)), _MM_HINT_T0);\n#endif\n                            tableint cand = datal[i];\n                            dist_t d = fstdistfunc_(dataPoint, getDataByInternalId(cand), dist_func_param_, local_state_ptr);\n                            if (d < curdist) {\n                                curdist = d;\n                                currObj = cand;\n                                changed = true;\n                            }\n                        }\n                    }\n                }\n            }\n\n            if (dataPointLevel > maxLevel)\n                throw std::runtime_error(\"Level of item to be updated cannot be bigger than max level\");\n\n            for (int level = dataPointLevel; level >= 0; level--) {\n                std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> topCandidates = searchBaseLayer(\n                        currObj, dataPoint, level, local_state_ptr);\n\n                std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> filteredTopCandidates;\n                while (topCandidates.size() > 0) {\n                    if (topCandidates.top().second != dataPointInternalId)\n                        filteredTopCandidates.push(topCandidates.top());\n\n                    topCandidates.pop();\n                }\n\n                // Since element_levels_ is being used to get `dataPointLevel`, there could be cases where `topCandidates` could just contains entry point itself.\n                // To prevent self loops, the `topCandidates` is filtered and thus can be empty.\n                if (filteredTopCandidates.size() > 0) {\n                    bool epDeleted = isMarkedDeleted(entryPointInternalId);\n                    if (epDeleted) {\n                        filteredTopCandidates.emplace(fstdistfunc_(dataPoint, getDataByInternalId(entryPointInternalId), dist_func_param_, local_state_ptr), entryPointInternalId);\n                        if (filteredTopCandidates.size() > ef_construction_)\n                            filteredTopCandidates.pop();\n                    }\n\n                    currObj = mutuallyConnectNewElement(dataPoint, dataPointInternalId, filteredTopCandidates, level, true, local_state_ptr);\n                }\n            }\n        }\n\n        std::vector<tableint> getConnectionsWithLock(tableint internalId, int level) {\n            std::unique_lock <std::mutex> lock(link_list_locks_[internalId]);\n            unsigned int *data = get_linklist_at_level(internalId, level);\n            int size = getListCount(data);\n            std::vector<tableint> result(size);\n            tableint *ll = (tableint *) (data + 1);\n            memcpy(result.data(), ll,size * sizeof(tableint));\n            return result;\n        };\n\n        tableint addPoint(const void *data_point, labeltype label, int level, size_t batch_index) {\n\n            tableint cur_c = 0;\n            local_state_t local_state;\n            local_state.batch_index = batch_index;\n\n            {\n                // Checking if the element with the same label already exists\n                // if so, updating it *instead* of creating a new element.\n                std::unique_lock <std::mutex> templock_curr(cur_element_count_guard_);\n                auto search = label_lookup_.find(label);\n                if (search != label_lookup_.end()) {\n                    tableint existingInternalId = search->second;\n                    templock_curr.unlock();\n\n                    std::unique_lock <std::mutex> lock_el_update(link_list_update_locks_[(existingInternalId & (max_update_element_locks - 1))]);\n\n                    if (isMarkedDeleted(existingInternalId)) {\n                        unmarkDeletedInternal(existingInternalId);\n                    }\n                    updatePoint(data_point, existingInternalId, 1.0, &local_state);\n\n                    return existingInternalId;\n                }\n\n                if (cur_element_count >= max_elements_) {\n                    throw std::runtime_error(\"The number of elements exceeds the specified limit\");\n                };\n\n                cur_c = cur_element_count;\n                cur_element_count++;\n                label_lookup_[label] = cur_c;\n            }\n\n            // Take update lock to prevent race conditions on an element with insertion/update at the same time.\n            std::unique_lock <std::mutex> lock_el_update(link_list_update_locks_[(cur_c & (max_update_element_locks - 1))]);\n            std::unique_lock <std::mutex> lock_el(link_list_locks_[cur_c]);\n            int curlevel = getRandomLevel(mult_);\n            if (level > 0)\n                curlevel = level;\n\n            element_levels_[cur_c] = curlevel;\n\n\n            std::unique_lock <std::mutex> templock(global);\n            int maxlevelcopy = maxlevel_;\n            if (curlevel <= maxlevelcopy)\n                templock.unlock();\n            tableint currObj = enterpoint_node_;\n            tableint enterpoint_copy = enterpoint_node_;\n\n\n            memset(data_level0_memory_ + cur_c * size_data_per_element_ + offsetLevel0_, 0, size_data_per_element_);\n\n            // Initialisation of the data and label\n            memcpy(getExternalLabeLp(cur_c), &label, sizeof(labeltype));\n            memcpy(getDataByInternalId(cur_c), data_point, data_size_);\n\n\n            if (curlevel) {\n                linkLists_[cur_c] = (char *) malloc(size_links_per_element_ * curlevel + 1);\n                if (linkLists_[cur_c] == nullptr)\n                    throw std::runtime_error(\"Not enough memory: addPoint failed to allocate linklist\");\n                memset(linkLists_[cur_c], 0, size_links_per_element_ * curlevel + 1);\n            }\n\n            if ((signed)currObj != -1) {\n\n                if (curlevel < maxlevelcopy) {\n\n                    dist_t curdist = fstdistfunc_(data_point, getDataByInternalId(currObj), dist_func_param_, &local_state);\n                    for (int level = maxlevelcopy; level > curlevel; level--) {\n\n\n                        bool changed = true;\n                        while (changed) {\n                            changed = false;\n                            unsigned int *data;\n                            std::unique_lock <std::mutex> lock(link_list_locks_[currObj]);\n                            data = get_linklist(currObj,level);\n                            int size = getListCount(data);\n\n                            tableint *datal = (tableint *) (data + 1);\n                            for (int i = 0; i < size; i++) {\n                                tableint cand = datal[i];\n                                if (cand < 0 || cand > max_elements_)\n                                    throw std::runtime_error(\"cand error\");\n                                dist_t d = fstdistfunc_(data_point, getDataByInternalId(cand), dist_func_param_, &local_state);\n                                if (d < curdist) {\n                                    curdist = d;\n                                    currObj = cand;\n                                    changed = true;\n                                }\n                            }\n                        }\n                    }\n                }\n\n                bool epDeleted = isMarkedDeleted(enterpoint_copy);\n                for (int level = std::min(curlevel, maxlevelcopy); level >= 0; level--) {\n                    if (level > maxlevelcopy || level < 0)  // possible?\n                        throw std::runtime_error(\"Level error\");\n\n                    std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> top_candidates = searchBaseLayer(\n                        currObj, data_point, level, &local_state);\n                    if (epDeleted) {\n                        top_candidates.emplace(fstdistfunc_(data_point, getDataByInternalId(enterpoint_copy), dist_func_param_, &local_state), enterpoint_copy);\n                        if (top_candidates.size() > ef_construction_)\n                            top_candidates.pop();\n                    }\n                    currObj = mutuallyConnectNewElement(data_point, cur_c, top_candidates, level, false, &local_state);\n                }\n\n\n            } else {\n                // Do nothing for the first element\n                enterpoint_node_ = 0;\n                maxlevel_ = curlevel;\n\n            }\n\n            //Releasing lock for the maximum level\n            if (curlevel > maxlevelcopy) {\n                enterpoint_node_ = cur_c;\n                maxlevel_ = curlevel;\n            }\n            return cur_c;\n        };\n\n        std::priority_queue<std::pair<dist_t, labeltype >>\n        searchKnn(const void *query_data, size_t k, size_t batch_index) const {\n            std::priority_queue<std::pair<dist_t, labeltype >> result;\n            if (cur_element_count == 0) return result;\n\n            local_state_t local_state;\n            local_state.batch_index = batch_index;\n\n            tableint currObj = enterpoint_node_;\n            dist_t curdist = fstdistfunc_(query_data, getDataByInternalId(enterpoint_node_), dist_func_param_, &local_state);\n\n            for (int level = maxlevel_; level > 0; level--) {\n                bool changed = true;\n                while (changed) {\n                    changed = false;\n                    unsigned int *data;\n\n                    data = (unsigned int *) get_linklist(currObj, level);\n                    int size = getListCount(data);\n                    metric_hops++;\n                    metric_distance_computations+=size;\n\n                    tableint *datal = (tableint *)(data + 1);\n                    for (int i = 0; i < size; i++) {\n                        tableint cand = datal[i];\n                        if (cand < 0 || cand > max_elements_)\n                            throw std::runtime_error(\"cand error\");\n          dist_t d = fstdistfunc_(query_data, getDataByInternalId(cand),\n                                  dist_func_param_, &local_state);\n\n                        if (d < curdist) {\n                            curdist = d;\n                            currObj = cand;\n                            changed = true;\n                        }\n                    }\n                }\n            }\n\n            std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> top_candidates;\n            if (num_deleted_) {\n                top_candidates=searchBaseLayerST<true,true>(\n                        currObj, query_data, std::max(ef_, k), &local_state);\n            }\n            else{\n                top_candidates=searchBaseLayerST<false,true>(\n                        currObj, query_data, std::max(ef_, k), &local_state);\n            }\n\n            while (top_candidates.size() > k) {\n                top_candidates.pop();\n            }\n            while (top_candidates.size() > 0) {\n                std::pair<dist_t, tableint> rez = top_candidates.top();\n                result.push(std::pair<dist_t, labeltype>(rez.first, getExternalLabel(rez.second)));\n                top_candidates.pop();\n            }\n            return result;\n        };\n\n        std::priority_queue<std::pair<dist_t, labeltype>>\n        searchKnnWithFilter(const void *query_data, const binary_fuse16_t *filter,\n                            size_t k, size_t batch_index) const {\n            std::priority_queue<std::pair<dist_t, labeltype>> result;\n            if (cur_element_count == 0)\n            return result;\n            local_state_t local_state;\n            local_state.batch_index = batch_index;\n\n            tableint currObj = enterpoint_node_;\n            dist_t curdist =\n                fstdistfunc_(query_data, getDataByInternalId(enterpoint_node_),\n                            dist_func_param_, &local_state);\n\n            for (int level = maxlevel_; level > 0; level--) {\n            bool changed = true;\n            while (changed) {\n                changed = false;\n                unsigned int *data;\n\n                data = (unsigned int *)get_linklist(currObj, level);\n                int size = getListCount(data);\n                metric_hops++;\n                metric_distance_computations += size;\n\n                tableint *datal = (tableint *)(data + 1);\n                for (int i = 0; i < size; i++) {\n                tableint cand = datal[i];\n                if (cand < 0 || cand > max_elements_)\n                    throw std::runtime_error(\"cand error\");\n                dist_t d = fstdistfunc_(query_data, getDataByInternalId(cand),\n                                        dist_func_param_, &local_state);\n\n                if (d < curdist) {\n                    curdist = d;\n                    currObj = cand;\n                    changed = true;\n                }\n                }\n            }\n            }\n\n            std::priority_queue<std::pair<dist_t, tableint>,\n                                std::vector<std::pair<dist_t, tableint>>,\n                                CompareByFirst>\n                top_candidates;\n            if (has_deletions_) {\n            top_candidates = searchBaseLayerSTWithFilter<true, true>(\n                currObj, query_data, filter, std::max(ef_, k), &local_state);\n            } else {\n            top_candidates = searchBaseLayerSTWithFilter<false, true>(\n                currObj, query_data, filter, std::max(ef_, k), &local_state);\n            }\n\n            while (top_candidates.size() > k) {\n            top_candidates.pop();\n            }\n            while (top_candidates.size() > 0) {\n            std::pair<dist_t, tableint> rez = top_candidates.top();\n            result.push(std::pair<dist_t, labeltype>(rez.first,\n                                                    getExternalLabel(rez.second)));\n            top_candidates.pop();\n            }\n            return result;\n        };\n\n        void checkIntegrity(){\n            int connections_checked=0;\n            std::vector <int > inbound_connections_num(cur_element_count,0);\n            for(int i = 0;i < cur_element_count; i++){\n                for(int l = 0;l <= element_levels_[i]; l++){\n                    linklistsizeint *ll_cur = get_linklist_at_level(i,l);\n                    int size = getListCount(ll_cur);\n                    tableint *data = (tableint *) (ll_cur + 1);\n                    std::unordered_set<tableint> s;\n                    for (int j=0; j<size; j++){\n                        assert(data[j] > 0);\n                        assert(data[j] < cur_element_count);\n                        assert (data[j] != i);\n                        inbound_connections_num[data[j]]++;\n                        s.insert(data[j]);\n                        connections_checked++;\n\n                    }\n                    assert(s.size() == size);\n                }\n            }\n            if(cur_element_count > 1){\n                int min1=inbound_connections_num[0], max1=inbound_connections_num[0];\n                for(int i=0; i < cur_element_count; i++){\n                    assert(inbound_connections_num[i] > 0);\n                    min1=std::min(inbound_connections_num[i],min1);\n                    max1=std::max(inbound_connections_num[i],max1);\n                }\n                std::cout << \"Min inbound: \" << min1 << \", Max inbound:\" << max1 << \"\\n\";\n            }\n            std::cout << \"integrity ok, checked \" << connections_checked << \" connections\\n\";\n\n        }\n\n    };\n\n}\n"
  },
  {
    "path": "include/hnswlib/hnswlib.h",
    "content": "#pragma once\n#ifndef NO_MANUAL_VECTORIZATION\n#ifdef __SSE__\n#define USE_SSE\n#ifdef __AVX__\n#define USE_AVX\n#ifdef __AVX512F__\n#define USE_AVX512\n#endif\n#endif\n#endif\n#endif\n\n#if defined(USE_AVX) || defined(USE_SSE)\n#ifdef _MSC_VER\n#include <intrin.h>\n#include <stdexcept>\n#include \"cpu_x86.h\"\nvoid cpu_x86::cpuid(int32_t out[4], int32_t eax, int32_t ecx) {\n    __cpuidex(out, eax, ecx);\n}\n__int64 xgetbv(unsigned int x) {\n    return _xgetbv(x);\n}\n#else\n#include <x86intrin.h>\n#include <cpuid.h>\n#include <stdint.h>\nvoid cpuid(int32_t cpuInfo[4], int32_t eax, int32_t ecx) {\n    __cpuid_count(eax, ecx, cpuInfo[0], cpuInfo[1], cpuInfo[2], cpuInfo[3]);\n}\nuint64_t xgetbv(unsigned int index) {\n    uint32_t eax, edx;\n    __asm__ __volatile__(\"xgetbv\" : \"=a\"(eax), \"=d\"(edx) : \"c\"(index));\n    return ((uint64_t)edx << 32) | eax;\n}\n#endif\n\n#if defined(USE_AVX512)\n#include <immintrin.h>\n#endif\n\n#if defined(__GNUC__)\n#define PORTABLE_ALIGN32 __attribute__((aligned(32)))\n#define PORTABLE_ALIGN64 __attribute__((aligned(64)))\n#else\n#define PORTABLE_ALIGN32 __declspec(align(32))\n#define PORTABLE_ALIGN64 __declspec(align(64))\n#endif\n\n// Adapted from https://github.com/Mysticial/FeatureDetector\n#define _XCR_XFEATURE_ENABLED_MASK  0\n\nbool AVXCapable() {\n    int cpuInfo[4];\n\n    // CPU support\n    cpuid(cpuInfo, 0, 0);\n    int nIds = cpuInfo[0];\n\n    bool HW_AVX = false;\n    if (nIds >= 0x00000001) {\n        cpuid(cpuInfo, 0x00000001, 0);\n        HW_AVX = (cpuInfo[2] & ((int)1 << 28)) != 0;\n    }\n\n    // OS support\n    cpuid(cpuInfo, 1, 0);\n\n    bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0;\n    bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0;\n\n    bool avxSupported = false;\n    if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {\n        uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);\n        avxSupported = (xcrFeatureMask & 0x6) == 0x6;\n    }\n    return HW_AVX && avxSupported;\n}\n\nbool AVX512Capable() {\n    if (!AVXCapable()) return false;\n\n    int cpuInfo[4];\n\n    // CPU support\n    cpuid(cpuInfo, 0, 0);\n    int nIds = cpuInfo[0];\n\n    bool HW_AVX512F = false;\n    if (nIds >= 0x00000007) { //  AVX512 Foundation\n        cpuid(cpuInfo, 0x00000007, 0);\n        HW_AVX512F = (cpuInfo[1] & ((int)1 << 16)) != 0;\n    }\n\n    // OS support\n    cpuid(cpuInfo, 1, 0);\n\n    bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0;\n    bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0;\n\n    bool avx512Supported = false;\n    if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {\n        uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);\n        avx512Supported = (xcrFeatureMask & 0xe6) == 0xe6;\n    }\n    return HW_AVX512F && avx512Supported;\n}\n#endif\n\n#include <iostream>\n#include <queue>\n#include <string.h>\n#include <vector>\n\nnamespace hnswlib {\n    typedef size_t labeltype;\n\n    typedef struct local_state_s {\n        size_t batch_index;\n    } local_state_t;\n\n    typedef struct pq_local_data_s {\n        float *data;\n        size_t batch_len;\n    } pq_local_data_t;\n\n    template <typename T>\n    class pairGreater {\n    public:\n        bool operator()(const T& p1, const T& p2) {\n            return p1.first > p2.first;\n        }\n    };\n\n    template<typename T>\n    static void writeBinaryPOD(std::ostream &out, const T &podRef) {\n        out.write((char *) &podRef, sizeof(T));\n    }\n\n    template<typename T>\n    static void readBinaryPOD(std::istream &in, T &podRef) {\n        in.read((char *) &podRef, sizeof(T));\n    }\n\n    template<typename MTYPE>\n    using DISTFUNC = MTYPE(*)(const void *, const void *, const void *, const local_state_t *);\n\n\n    template<typename MTYPE>\n    class SpaceInterface {\n    public:\n        //virtual void search(void *);\n        virtual size_t get_data_size() = 0;\n\n        virtual DISTFUNC<MTYPE> get_dist_func() = 0;\n\n        virtual void attach_local_data(const void *) = 0;\n\n        virtual void detach_local_data() = 0;\n\n        virtual void *get_dist_func_param() = 0;\n\n        virtual ~SpaceInterface() {}\n    };\n\n    template<typename dist_t>\n    class AlgorithmInterface {\n    public:\n        virtual void addPoint(const void *datapoint, labeltype label, size_t batch_index) = 0;\n        virtual std::priority_queue<std::pair<dist_t, labeltype >> searchKnn(const void *, size_t, size_t) const = 0;\n\n        // Return k nearest neighbor in the order of closer fist\n        virtual std::vector<std::pair<dist_t, labeltype>>\n            searchKnnCloserFirst(const void *query_data, size_t k, size_t batch_index) const;\n\n        virtual void saveIndex(const std::string &location)=0;\n        virtual ~AlgorithmInterface(){\n        }\n    };\n\n    template <typename dist_t>\n    std::vector<std::pair<dist_t, labeltype>>\n    AlgorithmInterface<dist_t>::searchKnnCloserFirst(const void *query_data, size_t k, size_t batch_index) const {\n        std::vector<std::pair<dist_t, labeltype>> result;\n\n        // here searchKnn returns the result in the order of further first\n        auto ret = searchKnn(query_data, k, batch_index);\n        {\n            size_t sz = ret.size();\n            result.resize(sz);\n            while (!ret.empty()) {\n                result[--sz] = ret.top();\n                ret.pop();\n            }\n        }\n\n        return result;\n    }\n}\n\n#include \"bruteforce.h\"\n#include \"fusefilter.h\"\n#include \"hnswalg.h\"\n#include \"space_ip.h\"\n#include \"space_l2.h\"\n#include \"space_pq.h\"\n"
  },
  {
    "path": "include/hnswlib/space_ip.h",
    "content": "#pragma once\n#include \"hnswlib.h\"\n\nnamespace hnswlib {\n\n    static float\n    InnerProduct(const void *pVect1, const void *pVect2, const void *qty_ptr, const local_state_t *local_state) {\n        size_t qty = *((size_t *) qty_ptr);\n        float res = 0;\n        for (unsigned i = 0; i < qty; i++) {\n            res += ((float *) pVect1)[i] * ((float *) pVect2)[i];\n        }\n        return res;\n\n    }\n\n    static float\n    InnerProductDistance(const void *pVect1, const void *pVect2, const void *qty_ptr, const local_state_t *local_state) {\n        return 1.0f - InnerProduct(pVect1, pVect2, qty_ptr, local_state);\n    }\n\n#if defined(USE_AVX)\n\n// Favor using AVX if available.\n    static float\n    InnerProductSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float PORTABLE_ALIGN32 TmpRes[8];\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n        size_t qty16 = qty / 16;\n        size_t qty4 = qty / 4;\n\n        const float *pEnd1 = pVect1 + 16 * qty16;\n        const float *pEnd2 = pVect1 + 4 * qty4;\n\n        __m256 sum256 = _mm256_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);\n\n            __m256 v1 = _mm256_loadu_ps(pVect1);\n            pVect1 += 8;\n            __m256 v2 = _mm256_loadu_ps(pVect2);\n            pVect2 += 8;\n            sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2));\n\n            v1 = _mm256_loadu_ps(pVect1);\n            pVect1 += 8;\n            v2 = _mm256_loadu_ps(pVect2);\n            pVect2 += 8;\n            sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2));\n        }\n\n        __m128 v1, v2;\n        __m128 sum_prod = _mm_add_ps(_mm256_extractf128_ps(sum256, 0), _mm256_extractf128_ps(sum256, 1));\n\n        while (pVect1 < pEnd2) {\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n        }\n\n        _mm_store_ps(TmpRes, sum_prod);\n        float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];;\n        return sum;\n    }\n\n    static float\n    InnerProductDistanceSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        return 1.0f - InnerProductSIMD4ExtAVX(pVect1v, pVect2v, qty_ptr, local_state);\n    }\n\n#endif\n\n#if defined(USE_SSE)\n\n    static float\n    InnerProductSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float PORTABLE_ALIGN32 TmpRes[8];\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n        size_t qty16 = qty / 16;\n        size_t qty4 = qty / 4;\n\n        const float *pEnd1 = pVect1 + 16 * qty16;\n        const float *pEnd2 = pVect1 + 4 * qty4;\n\n        __m128 v1, v2;\n        __m128 sum_prod = _mm_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n        }\n\n        while (pVect1 < pEnd2) {\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n        }\n\n        _mm_store_ps(TmpRes, sum_prod);\n        float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];\n\n        return sum;\n    }\n\n    static float\n    InnerProductDistanceSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        return 1.0f - InnerProductSIMD4ExtSSE(pVect1v, pVect2v, qty_ptr, local_state);\n    }\n\n#endif\n\n\n#if defined(USE_AVX512)\n\n    static float\n    InnerProductSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float PORTABLE_ALIGN64 TmpRes[16];\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n        size_t qty16 = qty / 16;\n\n\n        const float *pEnd1 = pVect1 + 16 * qty16;\n\n        __m512 sum512 = _mm512_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);\n\n            __m512 v1 = _mm512_loadu_ps(pVect1);\n            pVect1 += 16;\n            __m512 v2 = _mm512_loadu_ps(pVect2);\n            pVect2 += 16;\n            sum512 = _mm512_add_ps(sum512, _mm512_mul_ps(v1, v2));\n        }\n\n        _mm512_store_ps(TmpRes, sum512);\n        float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7] + TmpRes[8] + TmpRes[9] + TmpRes[10] + TmpRes[11] + TmpRes[12] + TmpRes[13] + TmpRes[14] + TmpRes[15];\n\n        return sum;\n    }\n\n    static float\n    InnerProductDistanceSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        return 1.0f - InnerProductSIMD16ExtAVX512(pVect1v, pVect2v, qty_ptr, local_state);\n    }\n\n#endif\n\n#if defined(USE_AVX)\n\n    static float\n    InnerProductSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float PORTABLE_ALIGN32 TmpRes[8];\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n        size_t qty16 = qty / 16;\n\n\n        const float *pEnd1 = pVect1 + 16 * qty16;\n\n        __m256 sum256 = _mm256_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);\n\n            __m256 v1 = _mm256_loadu_ps(pVect1);\n            pVect1 += 8;\n            __m256 v2 = _mm256_loadu_ps(pVect2);\n            pVect2 += 8;\n            sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2));\n\n            v1 = _mm256_loadu_ps(pVect1);\n            pVect1 += 8;\n            v2 = _mm256_loadu_ps(pVect2);\n            pVect2 += 8;\n            sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2));\n        }\n\n        _mm256_store_ps(TmpRes, sum256);\n        float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7];\n\n        return sum;\n    }\n\n    static float\n    InnerProductDistanceSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        return 1.0f - InnerProductSIMD16ExtAVX(pVect1v, pVect2v, qty_ptr, local_state);\n    }\n\n#endif\n\n#if defined(USE_SSE)\n\n    static float\n    InnerProductSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float PORTABLE_ALIGN32 TmpRes[8];\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n        size_t qty16 = qty / 16;\n\n        const float *pEnd1 = pVect1 + 16 * qty16;\n\n        __m128 v1, v2;\n        __m128 sum_prod = _mm_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2));\n        }\n        _mm_store_ps(TmpRes, sum_prod);\n        float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];\n\n        return sum;\n    }\n\n    static float\n    InnerProductDistanceSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        return 1.0f - InnerProductSIMD16ExtSSE(pVect1v, pVect2v, qty_ptr, local_state);\n    }\n\n#endif\n\n#if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)\n    DISTFUNC<float> InnerProductSIMD16Ext = InnerProductSIMD16ExtSSE;\n    DISTFUNC<float> InnerProductSIMD4Ext = InnerProductSIMD4ExtSSE;\n    DISTFUNC<float> InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtSSE;\n    DISTFUNC<float> InnerProductDistanceSIMD4Ext = InnerProductDistanceSIMD4ExtSSE;\n\n    static float\n    InnerProductDistanceSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        size_t qty = *((size_t *) qty_ptr);\n        size_t qty16 = qty >> 4 << 4;\n        float res = InnerProductSIMD16Ext(pVect1v, pVect2v, &qty16, local_state);\n        float *pVect1 = (float *) pVect1v + qty16;\n        float *pVect2 = (float *) pVect2v + qty16;\n\n        size_t qty_left = qty - qty16;\n        float res_tail = InnerProduct(pVect1, pVect2, &qty_left, local_state);\n        return 1.0f - (res + res_tail);\n    }\n\n    static float\n    InnerProductDistanceSIMD4ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        size_t qty = *((size_t *) qty_ptr);\n        size_t qty4 = qty >> 2 << 2;\n\n        float res = InnerProductSIMD4Ext(pVect1v, pVect2v, &qty4, local_state);\n        size_t qty_left = qty - qty4;\n\n        float *pVect1 = (float *) pVect1v + qty4;\n        float *pVect2 = (float *) pVect2v + qty4;\n        float res_tail = InnerProduct(pVect1, pVect2, &qty_left, local_state);\n\n        return 1.0f - (res + res_tail);\n    }\n#endif\n\n    class InnerProductSpace : public SpaceInterface<float> {\n\n        DISTFUNC<float> fstdistfunc_;\n        size_t data_size_;\n        size_t dim_;\n    public:\n        InnerProductSpace(size_t dim) {\n            fstdistfunc_ = InnerProductDistance;\n    #if defined(USE_AVX) || defined(USE_SSE) || defined(USE_AVX512)\n        #if defined(USE_AVX512)\n            if (AVX512Capable()) {\n                InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX512;\n                InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX512;\n            } else if (AVXCapable()) {\n                InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;\n                InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX;\n            }\n        #elif defined(USE_AVX)\n            if (AVXCapable()) {\n                InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;\n                InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX;\n            }\n        #endif\n        #if defined(USE_AVX)\n            if (AVXCapable()) {\n                InnerProductSIMD4Ext = InnerProductSIMD4ExtAVX;\n                InnerProductDistanceSIMD4Ext = InnerProductDistanceSIMD4ExtAVX;\n            }\n        #endif\n\n            if (dim % 16 == 0)\n                fstdistfunc_ = InnerProductDistanceSIMD16Ext;\n            else if (dim % 4 == 0)\n                fstdistfunc_ = InnerProductDistanceSIMD4Ext;\n            else if (dim > 16)\n                fstdistfunc_ = InnerProductDistanceSIMD16ExtResiduals;\n            else if (dim > 4)\n                fstdistfunc_ = InnerProductDistanceSIMD4ExtResiduals;\n    #endif\n            dim_ = dim;\n            data_size_ = dim * sizeof(float);\n        }\n\n        size_t get_data_size() {\n            return data_size_;\n        }\n\n        DISTFUNC<float> get_dist_func() {\n            return fstdistfunc_;\n        }\n\n        void *get_dist_func_param() {\n            return &dim_;\n        }\n\n    // Not local state\n    void attach_local_data(const void *) {}\n\n    void detach_local_data() {}\n\n    ~InnerProductSpace() {}\n    };\n\n}\n"
  },
  {
    "path": "include/hnswlib/space_l2.h",
    "content": "#pragma once\n#include \"hnswlib.h\"\n\nnamespace hnswlib {\n\n    static float\n    L2Sqr(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n        float res = 0;\n        for (size_t i = 0; i < qty; i++) {\n            float t = *pVect1 - *pVect2;\n            pVect1++;\n            pVect2++;\n            res += t * t;\n        }\n        return (res);\n    }\n\n#if defined(USE_AVX512)\n\n    // Favor using AVX512 if available.\n    static float\n    L2SqrSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n        float PORTABLE_ALIGN64 TmpRes[16];\n        size_t qty16 = qty >> 4;\n\n        const float *pEnd1 = pVect1 + (qty16 << 4);\n\n        __m512 diff, v1, v2;\n        __m512 sum = _mm512_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            v1 = _mm512_loadu_ps(pVect1);\n            pVect1 += 16;\n            v2 = _mm512_loadu_ps(pVect2);\n            pVect2 += 16;\n            diff = _mm512_sub_ps(v1, v2);\n            // sum = _mm512_fmadd_ps(diff, diff, sum);\n            sum = _mm512_add_ps(sum, _mm512_mul_ps(diff, diff));\n        }\n\n        _mm512_store_ps(TmpRes, sum);\n        float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] +\n                TmpRes[7] + TmpRes[8] + TmpRes[9] + TmpRes[10] + TmpRes[11] + TmpRes[12] +\n                TmpRes[13] + TmpRes[14] + TmpRes[15];\n\n        return (res);\n}\n#endif\n\n#if defined(USE_AVX)\n\n    // Favor using AVX if available.\n    static float\n    L2SqrSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n        float PORTABLE_ALIGN32 TmpRes[8];\n        size_t qty16 = qty >> 4;\n\n        const float *pEnd1 = pVect1 + (qty16 << 4);\n\n        __m256 diff, v1, v2;\n        __m256 sum = _mm256_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            v1 = _mm256_loadu_ps(pVect1);\n            pVect1 += 8;\n            v2 = _mm256_loadu_ps(pVect2);\n            pVect2 += 8;\n            diff = _mm256_sub_ps(v1, v2);\n            sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff));\n\n            v1 = _mm256_loadu_ps(pVect1);\n            pVect1 += 8;\n            v2 = _mm256_loadu_ps(pVect2);\n            pVect2 += 8;\n            diff = _mm256_sub_ps(v1, v2);\n            sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff));\n        }\n\n        _mm256_store_ps(TmpRes, sum);\n        return TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7];\n    }\n\n#endif\n\n#if defined(USE_SSE)\n\n    static float\n    L2SqrSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n        float PORTABLE_ALIGN32 TmpRes[8];\n        size_t qty16 = qty >> 4;\n\n        const float *pEnd1 = pVect1 + (qty16 << 4);\n\n        __m128 diff, v1, v2;\n        __m128 sum = _mm_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            diff = _mm_sub_ps(v1, v2);\n            sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            diff = _mm_sub_ps(v1, v2);\n            sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            diff = _mm_sub_ps(v1, v2);\n            sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff));\n\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            diff = _mm_sub_ps(v1, v2);\n            sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff));\n        }\n\n        _mm_store_ps(TmpRes, sum);\n        return TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];\n    }\n#endif\n\n#if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)\n    DISTFUNC<float> L2SqrSIMD16Ext = L2SqrSIMD16ExtSSE;\n\n    static float\n    L2SqrSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        size_t qty = *((size_t *) qty_ptr);\n        size_t qty16 = qty >> 4 << 4;\n        float res = L2SqrSIMD16Ext(pVect1v, pVect2v, &qty16, local_state);\n        float *pVect1 = (float *) pVect1v + qty16;\n        float *pVect2 = (float *) pVect2v + qty16;\n\n        size_t qty_left = qty - qty16;\n        float res_tail = L2Sqr(pVect1, pVect2, &qty_left, local_state);\n        return (res + res_tail);\n    }\n#endif\n\n\n#if defined(USE_SSE)\n    static float\n    L2SqrSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        float PORTABLE_ALIGN32 TmpRes[8];\n        float *pVect1 = (float *) pVect1v;\n        float *pVect2 = (float *) pVect2v;\n        size_t qty = *((size_t *) qty_ptr);\n\n\n        size_t qty4 = qty >> 2;\n\n        const float *pEnd1 = pVect1 + (qty4 << 2);\n\n        __m128 diff, v1, v2;\n        __m128 sum = _mm_set1_ps(0);\n\n        while (pVect1 < pEnd1) {\n            v1 = _mm_loadu_ps(pVect1);\n            pVect1 += 4;\n            v2 = _mm_loadu_ps(pVect2);\n            pVect2 += 4;\n            diff = _mm_sub_ps(v1, v2);\n            sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff));\n        }\n        _mm_store_ps(TmpRes, sum);\n        return TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];\n    }\n\n    static float\n    L2SqrSIMD4ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr, const local_state_t *local_state) {\n        size_t qty = *((size_t *) qty_ptr);\n        size_t qty4 = qty >> 2 << 2;\n\n        float res = L2SqrSIMD4Ext(pVect1v, pVect2v, &qty4, local_state);\n        size_t qty_left = qty - qty4;\n\n        float *pVect1 = (float *) pVect1v + qty4;\n        float *pVect2 = (float *) pVect2v + qty4;\n        float res_tail = L2Sqr(pVect1, pVect2, &qty_left, local_state);\n\n        return (res + res_tail);\n    }\n#endif\n\n    class L2Space : public SpaceInterface<float> {\n\n        DISTFUNC<float> fstdistfunc_;\n        size_t data_size_;\n        size_t dim_;\n    public:\n        L2Space(size_t dim) {\n            fstdistfunc_ = L2Sqr;\n    #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)\n        #if defined(USE_AVX512)\n            if (AVX512Capable())\n                L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX512;\n            else if (AVXCapable())\n                L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX;\n        #elif defined(USE_AVX)\n            if (AVXCapable())\n                L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX;\n        #endif\n\n            if (dim % 16 == 0)\n                fstdistfunc_ = L2SqrSIMD16Ext;\n            else if (dim % 4 == 0)\n                fstdistfunc_ = L2SqrSIMD4Ext;\n            else if (dim > 16)\n                fstdistfunc_ = L2SqrSIMD16ExtResiduals;\n            else if (dim > 4)\n                fstdistfunc_ = L2SqrSIMD4ExtResiduals;\n    #endif\n            dim_ = dim;\n            data_size_ = dim * sizeof(float);\n        }\n\n        size_t get_data_size() {\n            return data_size_;\n        }\n\n        DISTFUNC<float> get_dist_func() {\n            return fstdistfunc_;\n        }\n\n        void *get_dist_func_param() {\n            return &dim_;\n        }\n\n        // Not local state\n        void attach_local_data(const void *) {}\n\n        void detach_local_data() {}\n\n        ~L2Space() {}\n    };\n\n    static int\n    L2SqrI4x(const void *__restrict pVect1, const void *__restrict pVect2, const void *__restrict qty_ptr, const local_state_t *__restrict local_state) {\n\n        size_t qty = *((size_t *) qty_ptr);\n        int res = 0;\n        unsigned char *a = (unsigned char *) pVect1;\n        unsigned char *b = (unsigned char *) pVect2;\n\n        qty = qty >> 2;\n        for (size_t i = 0; i < qty; i++) {\n\n            res += ((*a) - (*b)) * ((*a) - (*b));\n            a++;\n            b++;\n            res += ((*a) - (*b)) * ((*a) - (*b));\n            a++;\n            b++;\n            res += ((*a) - (*b)) * ((*a) - (*b));\n            a++;\n            b++;\n            res += ((*a) - (*b)) * ((*a) - (*b));\n            a++;\n            b++;\n        }\n        return (res);\n    }\n\n    static int L2SqrI(const void* __restrict pVect1, const void* __restrict pVect2, const void* __restrict qty_ptr, const local_state_t *__restrict local_state) {\n        size_t qty = *((size_t*)qty_ptr);\n        int res = 0;\n        unsigned char* a = (unsigned char*)pVect1;\n        unsigned char* b = (unsigned char*)pVect2;\n\n        for(size_t i = 0; i < qty; i++)\n        {\n            res += ((*a) - (*b)) * ((*a) - (*b));\n            a++;\n            b++;\n        }\n        return (res);\n    }\n\n    class L2SpaceI : public SpaceInterface<int> {\n\n        DISTFUNC<int> fstdistfunc_;\n        size_t data_size_;\n        size_t dim_;\n    public:\n        L2SpaceI(size_t dim) {\n            if(dim % 4 == 0) {\n                fstdistfunc_ = L2SqrI4x;\n            }\n            else {\n                fstdistfunc_ = L2SqrI;\n            }\n            dim_ = dim;\n            data_size_ = dim * sizeof(unsigned char);\n        }\n\n        size_t get_data_size() {\n            return data_size_;\n        }\n\n        DISTFUNC<int> get_dist_func() {\n            return fstdistfunc_;\n        }\n\n        void *get_dist_func_param() {\n            return &dim_;\n        }\n\n        // Not local state\n        void attach_local_data(const void *) {}\n\n        void detach_local_data() {}\n\n        ~L2SpaceI() {}\n    };\n\n\n}\n"
  },
  {
    "path": "include/hnswlib/space_pq.h",
    "content": "#pragma once\n#include \"hnswlib.h\"\n#include <math.h>\n#include <memory>\n#include <stdint.h>\nnamespace hnswlib {\n\ntypedef struct pq_dist_param_s {\n  size_t n_subvectors;\n  size_t n_clusters;\n  size_t batch_len;\n  float *batch_dtable;\n} pq_dist_param_t;\n\ntemplate <typename CODETYPE>\nstatic float PQLookup(const void *pVect1v, const void *pVect2v,\n                      const void *qty_ptr, const local_state_t *local_state) {\n  CODETYPE *pVect2 = (CODETYPE *)pVect2v;\n  pq_dist_param_t *qty = (pq_dist_param_t *)qty_ptr;\n  if (qty->batch_len <= local_state->batch_index ||\n      qty->batch_dtable == nullptr) {\n    // Since all the batch_index actually manage by us\n    throw std::runtime_error(\"Row index exceeds or batch distance table \"\n                             \"uninitialized, most likely an internal bug!\");\n  }\n  size_t n_clusters = qty->n_clusters;\n  size_t row_step =\n      (qty->n_clusters * qty->n_subvectors) * local_state->batch_index;\n  const float *dtable = qty->batch_dtable;\n  float res = 0;\n\n  for (size_t i = 0; i < qty->n_subvectors; i++) {\n    res += dtable[row_step + i * n_clusters + (*pVect2)];\n    pVect2++;\n  }\n  return res;\n}\n\ntemplate <typename CODETYPE> class PQ_Space : public SpaceInterface<float> {\n  DISTFUNC<float> fstdistfunc_;\n  size_t data_size_, d_subvectors;\n  pq_dist_param_t param;\n  bool ip_enable;\n\npublic:\n  PQ_Space(const std::string &space_name, size_t n_subvectors,\n           size_t n_clusters, size_t d_subvectors, float *codebook)\n      : d_subvectors(d_subvectors) {\n    param.n_subvectors = n_subvectors;\n    param.n_clusters = n_clusters;\n    data_size_ = n_subvectors * sizeof(CODETYPE);\n    fstdistfunc_ = PQLookup<CODETYPE>;\n  }\n\n  void attach_local_data(const void *local_data) {\n    hnswlib::pq_local_data_t *pq_param = (hnswlib::pq_local_data_t *)local_data;\n    param.batch_dtable = pq_param->data;\n    param.batch_len = pq_param->batch_len;\n  }\n\n  void detach_local_data() {\n    param.batch_dtable = nullptr;\n    param.batch_len = 0;\n  };\n\n  size_t get_data_size() { return data_size_; }\n\n  DISTFUNC<float> get_dist_func() { return fstdistfunc_; }\n\n  void *get_dist_func_param() { return &param; }\n\n  ~PQ_Space() {}\n};\n} // namespace hnswlib\n"
  },
  {
    "path": "include/hnswlib/visited_list_pool.h",
    "content": "#pragma once\n\n#include <mutex>\n#include <string.h>\n#include <deque>\n\nnamespace hnswlib {\n    typedef unsigned short int vl_type;\n\n    class VisitedList {\n    public:\n        vl_type curV;\n        vl_type *mass;\n        unsigned int numelements;\n\n        VisitedList(int numelements1) {\n            curV = -1;\n            numelements = numelements1;\n            mass = new vl_type[numelements];\n        }\n\n        void reset() {\n            curV++;\n            if (curV == 0) {\n                memset(mass, 0, sizeof(vl_type) * numelements);\n                curV++;\n            }\n        };\n\n        ~VisitedList() { delete[] mass; }\n    };\n///////////////////////////////////////////////////////////\n//\n// Class for multi-threaded pool-management of VisitedLists\n//\n/////////////////////////////////////////////////////////\n\n    class VisitedListPool {\n        std::deque<VisitedList *> pool;\n        std::mutex poolguard;\n        int numelements;\n\n    public:\n        VisitedListPool(int initmaxpools, int numelements1) {\n            numelements = numelements1;\n            for (int i = 0; i < initmaxpools; i++)\n                pool.push_front(new VisitedList(numelements));\n        }\n\n        VisitedList *getFreeVisitedList() {\n            VisitedList *rez;\n            {\n                std::unique_lock <std::mutex> lock(poolguard);\n                if (pool.size() > 0) {\n                    rez = pool.front();\n                    pool.pop_front();\n                } else {\n                    rez = new VisitedList(numelements);\n                }\n            }\n            rez->reset();\n            return rez;\n        };\n\n        void releaseVisitedList(VisitedList *vl) {\n            std::unique_lock <std::mutex> lock(poolguard);\n            pool.push_front(vl);\n        };\n\n        ~VisitedListPool() {\n            while (pool.size()) {\n                VisitedList *rez = pool.front();\n                pool.pop_front();\n                delete rez;\n            }\n        };\n    };\n}\n\n"
  },
  {
    "path": "notebooks/fashion_product_search.ipynb",
    "content": "{\n  \"nbformat\": 4,\n  \"nbformat_minor\": 0,\n  \"metadata\": {\n    \"colab\": {\n      \"name\": \"fashion_product_search.ipynb\",\n      \"provenance\": [],\n      \"collapsed_sections\": []\n    },\n    \"kernelspec\": {\n      \"name\": \"python3\",\n      \"display_name\": \"Python 3\"\n    },\n    \"language_info\": {\n      \"name\": \"python\"\n    }\n  },\n  \"cells\": [\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"<p><img alt=\\\"Colaboratory logo\\\" height=\\\"45px\\\" src=\\\"https://colab.research.google.com/img/colab_favicon.ico\\\" align=\\\"left\\\" hspace=\\\"10px\\\" vspace=\\\"0px\\\"></p>\\n\",\n        \"\\n\",\n        \"<h1>AnnLite Powered E-commerce Product Search</h1>\\n\",\n        \"\\n\",\n        \"<div align=\\\"left\\\">\\n\",\n        \"<a href=\\\"https://github.com/jina-ai/annlite/blob/main/notebooks/fashion_product_search.ipynb\\\" role=\\\"button\\\"><img src=\\\"https://img.shields.io/static/v1?label=&amp;message=View%20On%20GitHub&amp;color=586069&amp;logo=github&amp;labelColor=2f363d\\\"></a>&nbsp;\\n\",\n        \"<a href=\\\"https://colab.research.google.com/github/jina-ai/annlite/blob/main/notebooks/fashion_product_search.ipynb\\\"><img src=\\\"https://colab.research.google.com/assets/colab-badge.svg\\\" alt=\\\"Open In Colab\\\"></a>\\n\",\n        \"</div>\"\n      ],\n      \"metadata\": {\n        \"id\": \"sXBlcqX4sxY9\"\n      }\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"## What is AnnLite?\\n\",\n        \"\\n\",\n        \"`AnnLite` is an  **Approximate Nearest Neighbor Search** (ANNS) library integrated with the Jina ecosystem.\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"This indexer is recommended to be used when an application requires **search with filters** applied on `Document` tags.\\n\",\n        \"The `filtering query language` is based on [MongoDB's query and projection operators](https://docs.mongodb.com/manual/reference/operator/query/). We currently support a subset of those selectors.\\n\",\n        \"The tags filters can be combined with `$and` and `$or`:\\n\",\n        \"\\n\",\n        \"- `$eq` - Equal to (number, string)\\n\",\n        \"- `$ne` - Not equal to (number, string)\\n\",\n        \"- `$gt` - Greater than (number)\\n\",\n        \"- `$gte` - Greater than or equal to (number)\\n\",\n        \"- `$lt` - Less than (number)\\n\",\n        \"- `$lte` - Less than or equal to (number)\\n\",\n        \"\\n\",\n        \"For example, we want to search for a product with a price no more than `50$`.\\n\",\n        \"```python\\n\",\n        \"index.search(query, filter={\\\"price\\\": {\\\"$lte\\\": 50}})\\n\",\n        \"```\\n\"\n      ],\n      \"metadata\": {\n        \"id\": \"nIbrboKnckJG\"\n      }\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"_ym-WauGOsto\"\n      },\n      \"source\": [\n        \"**Building a Neural Search System for Ecommerce Product Search**\\n\",\n        \"\\n\",\n        \"In this tutorial, we will use **annlite** as indexer to build a neural search engine for images of [Fasion Product Image (Small)](https://www.kaggle.com/paramaggarwal/fashion-product-images-small). This examples allows for **appling filters on various product attributes** when performing similarity search.\\n\",\n        \"\\n\",\n        \"![image](https://storage.googleapis.com/kaggle-datasets-images/175990/396802/720cd7ceb25eb130d0b873464f734370/data-original.png)\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"KpUu97Fq7v4T\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"\\n\",\n        \"## Preliminaries\\n\",\n        \"\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Download dataset**\\n\",\n        \"\\n\",\n        \"*Skip this if you've already downloaded them.*\\n\"\n      ],\n      \"metadata\": {\n        \"id\": \"uX3E944Mt_vq\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"uou66Zcw7X8g\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"outputId\": \"5ee59044-c0d8-42b0-e7bb-47aa978ffd8e\"\n      },\n      \"source\": [\n        \"!rm -rf data\\n\",\n        \"!pip install gdown\\n\",\n        \"\\n\",\n        \"import gdown\\n\",\n        \"\\n\",\n        \"# a file\\n\",\n        \"url = \\\"https://drive.google.com/file/d/1p26I3AaFO8bLU1PzDlGKUdM7eBVDrLjm/view?usp=sharing\\\"\\n\",\n        \"output = \\\"fashion.zip\\\"\\n\",\n        \"gdown.download(url, output, quiet=False, fuzzy=True)\\n\",\n        \"\\n\",\n        \"!unzip fashion.zip -d data > /dev/null\"\n      ],\n      \"execution_count\": 1,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"Requirement already satisfied: gdown in /usr/local/lib/python3.7/dist-packages (4.2.2)\\n\",\n            \"Requirement already satisfied: tqdm in /usr/local/lib/python3.7/dist-packages (from gdown) (4.63.0)\\n\",\n            \"Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.7/dist-packages (from gdown) (4.6.3)\\n\",\n            \"Requirement already satisfied: requests[socks] in /usr/local/lib/python3.7/dist-packages (from gdown) (2.23.0)\\n\",\n            \"Requirement already satisfied: filelock in /usr/local/lib/python3.7/dist-packages (from gdown) (3.6.0)\\n\",\n            \"Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from gdown) (1.15.0)\\n\",\n            \"Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests[socks]->gdown) (1.24.3)\\n\",\n            \"Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests[socks]->gdown) (3.0.4)\\n\",\n            \"Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests[socks]->gdown) (2.10)\\n\",\n            \"Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests[socks]->gdown) (2021.10.8)\\n\",\n            \"Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.7/dist-packages (from requests[socks]->gdown) (1.7.1)\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"Downloading...\\n\",\n            \"From: https://drive.google.com/uc?id=1p26I3AaFO8bLU1PzDlGKUdM7eBVDrLjm\\n\",\n            \"To: /content/fashion.zip\\n\",\n            \"100%|██████████| 6.55M/6.55M [00:01<00:00, 5.63MB/s]\\n\"\n          ]\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Install dependencies**\"\n      ],\n      \"metadata\": {\n        \"id\": \"Q32auOxQtKX2\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"!pip install pandas\\n\",\n        \"!pip install Pillow\\n\",\n        \"!pip install matplotlib\\n\",\n        \"!pip install torchvision\\n\",\n        \"\\n\",\n        \"!pip install jina\\n\",\n        \"!pip install annlite\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"Z4jpZDCCIubb\",\n        \"outputId\": \"238a4f82-26a9-4783-a273-a19a2dce9e58\"\n      },\n      \"execution_count\": 2,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (1.3.5)\\n\",\n            \"Requirement already satisfied: pytz>=2017.3 in /usr/local/lib/python3.7/dist-packages (from pandas) (2018.9)\\n\",\n            \"Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.7/dist-packages (from pandas) (1.21.5)\\n\",\n            \"Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas) (2.8.2)\\n\",\n            \"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas) (1.15.0)\\n\",\n            \"Requirement already satisfied: Pillow in /usr/local/lib/python3.7/dist-packages (7.1.2)\\n\",\n            \"Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (3.2.2)\\n\",\n            \"Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (3.0.7)\\n\",\n            \"Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (0.11.0)\\n\",\n            \"Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (1.3.2)\\n\",\n            \"Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (1.21.5)\\n\",\n            \"Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (2.8.2)\\n\",\n            \"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib) (1.15.0)\\n\",\n            \"Requirement already satisfied: torchvision in /usr/local/lib/python3.7/dist-packages (0.11.1+cu111)\\n\",\n            \"Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from torchvision) (1.21.5)\\n\",\n            \"Requirement already satisfied: pillow!=8.3.0,>=5.3.0 in /usr/local/lib/python3.7/dist-packages (from torchvision) (7.1.2)\\n\",\n            \"Requirement already satisfied: torch==1.10.0 in /usr/local/lib/python3.7/dist-packages (from torchvision) (1.10.0+cu111)\\n\",\n            \"Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from torch==1.10.0->torchvision) (3.10.0.2)\\n\",\n            \"Requirement already satisfied: jina in /usr/local/lib/python3.7/dist-packages (3.0.4)\\n\",\n            \"Requirement already satisfied: filelock in /usr/local/lib/python3.7/dist-packages (from jina) (3.6.0)\\n\",\n            \"Requirement already satisfied: aiofiles in /usr/local/lib/python3.7/dist-packages (from jina) (0.8.0)\\n\",\n            \"Requirement already satisfied: pydantic in /usr/local/lib/python3.7/dist-packages (from jina) (1.9.0)\\n\",\n            \"Requirement already satisfied: uvicorn[standard] in /usr/local/lib/python3.7/dist-packages (from jina) (0.17.5)\\n\",\n            \"Requirement already satisfied: aiostream in /usr/local/lib/python3.7/dist-packages (from jina) (0.4.4)\\n\",\n            \"Requirement already satisfied: protobuf>=3.19.1 in /usr/local/lib/python3.7/dist-packages (from jina) (3.19.4)\\n\",\n            \"Requirement already satisfied: fastapi in /usr/local/lib/python3.7/dist-packages (from jina) (0.74.1)\\n\",\n            \"Requirement already satisfied: rich in /usr/local/lib/python3.7/dist-packages (from jina) (11.2.0)\\n\",\n            \"Requirement already satisfied: websockets in /usr/local/lib/python3.7/dist-packages (from jina) (10.2)\\n\",\n            \"Requirement already satisfied: lz4<3.1.2 in /usr/local/lib/python3.7/dist-packages (from jina) (3.1.1)\\n\",\n            \"Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from jina) (2.23.0)\\n\",\n            \"Requirement already satisfied: cryptography in /usr/local/lib/python3.7/dist-packages (from jina) (36.0.1)\\n\",\n            \"Requirement already satisfied: aiohttp in /usr/local/lib/python3.7/dist-packages (from jina) (3.8.1)\\n\",\n            \"Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.7/dist-packages (from jina) (21.3)\\n\",\n            \"Requirement already satisfied: pathspec in /usr/local/lib/python3.7/dist-packages (from jina) (0.9.0)\\n\",\n            \"Requirement already satisfied: grpcio>=1.33.1 in /usr/local/lib/python3.7/dist-packages (from jina) (1.44.0)\\n\",\n            \"Requirement already satisfied: python-multipart in /usr/local/lib/python3.7/dist-packages (from jina) (0.0.5)\\n\",\n            \"Requirement already satisfied: kubernetes>=18.20.0 in /usr/local/lib/python3.7/dist-packages (from jina) (23.3.0)\\n\",\n            \"Requirement already satisfied: docarray>=0.7.0 in /usr/local/lib/python3.7/dist-packages (from jina) (0.8.11)\\n\",\n            \"Requirement already satisfied: pyyaml>=5.3.1 in /usr/local/lib/python3.7/dist-packages (from jina) (6.0)\\n\",\n            \"Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from jina) (1.21.5)\\n\",\n            \"Requirement already satisfied: uvloop in /usr/local/lib/python3.7/dist-packages (from jina) (0.16.0)\\n\",\n            \"Requirement already satisfied: docker in /usr/local/lib/python3.7/dist-packages (from jina) (5.0.3)\\n\",\n            \"Requirement already satisfied: six>=1.5.2 in /usr/local/lib/python3.7/dist-packages (from grpcio>=1.33.1->jina) (1.15.0)\\n\",\n            \"Requirement already satisfied: urllib3>=1.24.2 in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (1.24.3)\\n\",\n            \"Requirement already satisfied: setuptools>=21.0.0 in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (57.4.0)\\n\",\n            \"Requirement already satisfied: google-auth>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (1.35.0)\\n\",\n            \"Requirement already satisfied: websocket-client!=0.40.0,!=0.41.*,!=0.42.*,>=0.32.0 in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (1.3.1)\\n\",\n            \"Requirement already satisfied: python-dateutil>=2.5.3 in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (2.8.2)\\n\",\n            \"Requirement already satisfied: requests-oauthlib in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (1.3.1)\\n\",\n            \"Requirement already satisfied: certifi>=14.05.14 in /usr/local/lib/python3.7/dist-packages (from kubernetes>=18.20.0->jina) (2021.10.8)\\n\",\n            \"Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.0.1->kubernetes>=18.20.0->jina) (4.8)\\n\",\n            \"Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.0.1->kubernetes>=18.20.0->jina) (0.2.8)\\n\",\n            \"Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.0.1->kubernetes>=18.20.0->jina) (4.2.4)\\n\",\n            \"Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/local/lib/python3.7/dist-packages (from packaging>=20.0->jina) (3.0.7)\\n\",\n            \"Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /usr/local/lib/python3.7/dist-packages (from pyasn1-modules>=0.2.1->google-auth>=1.0.1->kubernetes>=18.20.0->jina) (0.4.8)\\n\",\n            \"Requirement already satisfied: charset-normalizer<3.0,>=2.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (2.0.12)\\n\",\n            \"Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (6.0.2)\\n\",\n            \"Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (1.3.0)\\n\",\n            \"Requirement already satisfied: yarl<2.0,>=1.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (1.7.2)\\n\",\n            \"Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (1.2.0)\\n\",\n            \"Requirement already satisfied: typing-extensions>=3.7.4 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (3.10.0.2)\\n\",\n            \"Requirement already satisfied: asynctest==0.13.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (0.13.0)\\n\",\n            \"Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (21.4.0)\\n\",\n            \"Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /usr/local/lib/python3.7/dist-packages (from aiohttp->jina) (4.0.2)\\n\",\n            \"Requirement already satisfied: idna>=2.0 in /usr/local/lib/python3.7/dist-packages (from yarl<2.0,>=1.0->aiohttp->jina) (2.10)\\n\",\n            \"Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.7/dist-packages (from cryptography->jina) (1.15.0)\\n\",\n            \"Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.12->cryptography->jina) (2.21)\\n\",\n            \"Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->jina) (3.0.4)\\n\",\n            \"Requirement already satisfied: starlette==0.17.1 in /usr/local/lib/python3.7/dist-packages (from fastapi->jina) (0.17.1)\\n\",\n            \"Requirement already satisfied: anyio<4,>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from starlette==0.17.1->fastapi->jina) (3.5.0)\\n\",\n            \"Requirement already satisfied: sniffio>=1.1 in /usr/local/lib/python3.7/dist-packages (from anyio<4,>=3.0.0->starlette==0.17.1->fastapi->jina) (1.2.0)\\n\",\n            \"Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from requests-oauthlib->kubernetes>=18.20.0->jina) (3.2.0)\\n\",\n            \"Requirement already satisfied: pygments<3.0.0,>=2.6.0 in /usr/local/lib/python3.7/dist-packages (from rich->jina) (2.6.1)\\n\",\n            \"Requirement already satisfied: colorama<0.5.0,>=0.4.0 in /usr/local/lib/python3.7/dist-packages (from rich->jina) (0.4.4)\\n\",\n            \"Requirement already satisfied: commonmark<0.10.0,>=0.9.0 in /usr/local/lib/python3.7/dist-packages (from rich->jina) (0.9.1)\\n\",\n            \"Requirement already satisfied: asgiref>=3.4.0 in /usr/local/lib/python3.7/dist-packages (from uvicorn[standard]->jina) (3.5.0)\\n\",\n            \"Requirement already satisfied: h11>=0.8 in /usr/local/lib/python3.7/dist-packages (from uvicorn[standard]->jina) (0.13.0)\\n\",\n            \"Requirement already satisfied: click>=7.0 in /usr/local/lib/python3.7/dist-packages (from uvicorn[standard]->jina) (7.1.2)\\n\",\n            \"Requirement already satisfied: watchgod>=0.6 in /usr/local/lib/python3.7/dist-packages (from uvicorn[standard]->jina) (0.7)\\n\",\n            \"Requirement already satisfied: python-dotenv>=0.13 in /usr/local/lib/python3.7/dist-packages (from uvicorn[standard]->jina) (0.19.2)\\n\",\n            \"Requirement already satisfied: httptools<0.4.0,>=0.2.0 in /usr/local/lib/python3.7/dist-packages (from uvicorn[standard]->jina) (0.3.0)\\n\",\n            \"Requirement already satisfied: annlite in /usr/local/lib/python3.7/dist-packages (0.3.0)\\n\",\n            \"Requirement already satisfied: protobuf>=3.13.0 in /usr/local/lib/python3.7/dist-packages (from annlite) (3.19.4)\\n\",\n            \"Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from annlite) (1.21.5)\\n\",\n            \"Requirement already satisfied: cython in /usr/local/lib/python3.7/dist-packages (from annlite) (0.29.28)\\n\",\n            \"Requirement already satisfied: click in /usr/local/lib/python3.7/dist-packages (from annlite) (7.1.2)\\n\",\n            \"Requirement already satisfied: docarray>=0.4.4 in /usr/local/lib/python3.7/dist-packages (from annlite) (0.8.11)\\n\",\n            \"Requirement already satisfied: loguru in /usr/local/lib/python3.7/dist-packages (from annlite) (0.6.0)\\n\",\n            \"Requirement already satisfied: lmdb in /usr/local/lib/python3.7/dist-packages (from annlite) (0.99)\\n\",\n            \"Requirement already satisfied: scikit-learn in /usr/local/lib/python3.7/dist-packages (from annlite) (1.0.2)\\n\",\n            \"Requirement already satisfied: rich in /usr/local/lib/python3.7/dist-packages (from docarray>=0.4.4->annlite) (11.2.0)\\n\",\n            \"Requirement already satisfied: colorama<0.5.0,>=0.4.0 in /usr/local/lib/python3.7/dist-packages (from rich->docarray>=0.4.4->annlite) (0.4.4)\\n\",\n            \"Requirement already satisfied: typing-extensions<5.0,>=3.7.4 in /usr/local/lib/python3.7/dist-packages (from rich->docarray>=0.4.4->annlite) (3.10.0.2)\\n\",\n            \"Requirement already satisfied: commonmark<0.10.0,>=0.9.0 in /usr/local/lib/python3.7/dist-packages (from rich->docarray>=0.4.4->annlite) (0.9.1)\\n\",\n            \"Requirement already satisfied: pygments<3.0.0,>=2.6.0 in /usr/local/lib/python3.7/dist-packages (from rich->docarray>=0.4.4->annlite) (2.6.1)\\n\",\n            \"Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->annlite) (3.1.0)\\n\",\n            \"Requirement already satisfied: scipy>=1.1.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->annlite) (1.4.1)\\n\",\n            \"Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn->annlite) (1.1.0)\\n\"\n          ]\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Prepare workspace**\"\n      ],\n      \"metadata\": {\n        \"id\": \"9u4eqV4WtQAG\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"lJ9ul55u4HOC\"\n      },\n      \"source\": [\n        \"!rm -rf workspace\\n\",\n        \"!mkdir workspace\"\n      ],\n      \"execution_count\": 3,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"metadata\": {\n        \"id\": \"oZXKzbcd_GD7\"\n      },\n      \"source\": [\n        \"---\\n\",\n        \"\\n\",\n        \"# Using **AnnLite** in E-Commerce Product Image Search\\n\"\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"metadata\": {\n        \"id\": \"r-dNxUGqEVzS\",\n        \"outputId\": \"8ab65e78-fb52-447a-8898-0d84fe747ae2\",\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        }\n      },\n      \"source\": [\n        \"import os\\n\",\n        \"import glob\\n\",\n        \"\\n\",\n        \"import pandas as pd\\n\",\n        \"\\n\",\n        \"from PIL import Image\\n\",\n        \"from jina import Document, DocumentArray\\n\",\n        \"\\n\",\n        \"from matplotlib import pyplot as plt\\n\",\n        \"from matplotlib.pyplot import imshow\\n\",\n        \"\\n\",\n        \"from annlite import AnnLite\\n\",\n        \"\\n\",\n        \"os.environ['JINA_LOG_LEVEL'] = 'DEBUG'\"\n      ],\n      \"execution_count\": 4,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"\\u001b[1;33mDeprecationWarning: The module numpy.dual is deprecated.  Instead of using dual, use the functions directly from numpy or scipy.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/fft/__init__.py:97)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.typeDict` is a deprecated alias for `np.sctypeDict`.\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/sparse/sputils.py:17)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\\n\",\n            \"Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/special/orthogonal.py:81)\\u001b[0m\\n\",\n            \"\\u001b[1;33mDeprecationWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this warning, use `int` by itself. Doing this will not modify any behavior and is safe. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to specify the precision. If you wish to review your current use, check the release note link for additional information.\\n\",\n            \"Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/scipy/special/orthogonal.py:81)\\u001b[0m\\n\"\n          ]\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Prepare Data**\"\n      ],\n      \"metadata\": {\n        \"id\": \"o8lxgKt2u4ux\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"MAX_NUM_DOCS = 200\\n\",\n        \"\\n\",\n        \"df = pd.read_csv('/content/data/data/styles.csv', warn_bad_lines=True, error_bad_lines=False)\\n\",\n        \"df = df.dropna()\\n\",\n        \"df['year'] = df['year'].astype(int)\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\"\n        },\n        \"id\": \"hpaJlIQ-u93z\",\n        \"outputId\": \"dd1ff1a2-6ebd-40a1-a81b-f1f739ad9d61\"\n      },\n      \"execution_count\": 5,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"\\u001b[1;33mFutureWarning: The error_bad_lines argument has been deprecated and will be removed in a future version.\\n\",\n            \"\\n\",\n            \"\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/IPython/core/interactiveshell.py:2882)\\u001b[0m\\n\",\n            \"\\u001b[1;33mFutureWarning: The warn_bad_lines argument has been deprecated and will be removed in a future version.\\n\",\n            \"\\n\",\n            \"\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/IPython/core/interactiveshell.py:2882)\\u001b[0m\\n\",\n            \"b'Skipping line 6044: expected 10 fields, saw 11\\\\nSkipping line 6569: expected 10 fields, saw 11\\\\nSkipping line 7399: expected 10 fields, saw 11\\\\nSkipping line 7939: expected 10 fields, saw 11\\\\nSkipping line 9026: expected 10 fields, saw 11\\\\nSkipping line 10264: expected 10 fields, saw 11\\\\nSkipping line 10427: expected 10 fields, saw 11\\\\nSkipping line 10905: expected 10 fields, saw 11\\\\nSkipping line 11373: expected 10 fields, saw 11\\\\nSkipping line 11945: expected 10 fields, saw 11\\\\nSkipping line 14112: expected 10 fields, saw 11\\\\nSkipping line 14532: expected 10 fields, saw 11\\\\nSkipping line 15076: expected 10 fields, saw 12\\\\nSkipping line 29906: expected 10 fields, saw 11\\\\nSkipping line 31625: expected 10 fields, saw 11\\\\nSkipping line 33020: expected 10 fields, saw 11\\\\nSkipping line 35748: expected 10 fields, saw 11\\\\nSkipping line 35962: expected 10 fields, saw 11\\\\nSkipping line 37770: expected 10 fields, saw 11\\\\nSkipping line 38105: expected 10 fields, saw 11\\\\nSkipping line 38275: expected 10 fields, saw 11\\\\nSkipping line 38404: expected 10 fields, saw 12\\\\n'\\n\",\n            \"\\u001b[1;33mSettingWithCopyWarning: \\n\",\n            \"A value is trying to be set on a copy of a slice from a DataFrame.\\n\",\n            \"Try using .loc[row_indexer,col_indexer] = value instead\\n\",\n            \"\\n\",\n            \"See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\\u001b[0m \\u001b[1;30m(raised from /usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:5)\\u001b[0m\\n\"\n          ]\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"df\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 423\n        },\n        \"id\": \"gkdL6ZXpvP7K\",\n        \"outputId\": \"5c4c160d-374d-4de9-ebde-0dc041fd298c\"\n      },\n      \"execution_count\": 6,\n      \"outputs\": [\n        {\n          \"output_type\": \"execute_result\",\n          \"data\": {\n            \"text/html\": [\n              \"\\n\",\n              \"  <div id=\\\"df-6f4ac1df-0406-441f-8085-9b5b8a657a4c\\\">\\n\",\n              \"    <div class=\\\"colab-df-container\\\">\\n\",\n              \"      <div>\\n\",\n              \"<style scoped>\\n\",\n              \"    .dataframe tbody tr th:only-of-type {\\n\",\n              \"        vertical-align: middle;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe tbody tr th {\\n\",\n              \"        vertical-align: top;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .dataframe thead th {\\n\",\n              \"        text-align: right;\\n\",\n              \"    }\\n\",\n              \"</style>\\n\",\n              \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n              \"  <thead>\\n\",\n              \"    <tr style=\\\"text-align: right;\\\">\\n\",\n              \"      <th></th>\\n\",\n              \"      <th>id</th>\\n\",\n              \"      <th>gender</th>\\n\",\n              \"      <th>masterCategory</th>\\n\",\n              \"      <th>subCategory</th>\\n\",\n              \"      <th>articleType</th>\\n\",\n              \"      <th>baseColour</th>\\n\",\n              \"      <th>season</th>\\n\",\n              \"      <th>year</th>\\n\",\n              \"      <th>usage</th>\\n\",\n              \"      <th>productDisplayName</th>\\n\",\n              \"    </tr>\\n\",\n              \"  </thead>\\n\",\n              \"  <tbody>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>0</th>\\n\",\n              \"      <td>15970</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Apparel</td>\\n\",\n              \"      <td>Topwear</td>\\n\",\n              \"      <td>Shirts</td>\\n\",\n              \"      <td>Navy Blue</td>\\n\",\n              \"      <td>Fall</td>\\n\",\n              \"      <td>2011</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Turtle Check Men Navy Blue Shirt</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>1</th>\\n\",\n              \"      <td>39386</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Apparel</td>\\n\",\n              \"      <td>Bottomwear</td>\\n\",\n              \"      <td>Jeans</td>\\n\",\n              \"      <td>Blue</td>\\n\",\n              \"      <td>Summer</td>\\n\",\n              \"      <td>2012</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Peter England Men Party Blue Jeans</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>2</th>\\n\",\n              \"      <td>59263</td>\\n\",\n              \"      <td>Women</td>\\n\",\n              \"      <td>Accessories</td>\\n\",\n              \"      <td>Watches</td>\\n\",\n              \"      <td>Watches</td>\\n\",\n              \"      <td>Silver</td>\\n\",\n              \"      <td>Winter</td>\\n\",\n              \"      <td>2016</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Titan Women Silver Watch</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>3</th>\\n\",\n              \"      <td>21379</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Apparel</td>\\n\",\n              \"      <td>Bottomwear</td>\\n\",\n              \"      <td>Track Pants</td>\\n\",\n              \"      <td>Black</td>\\n\",\n              \"      <td>Fall</td>\\n\",\n              \"      <td>2011</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Manchester United Men Solid Black Track Pants</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>4</th>\\n\",\n              \"      <td>53759</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Apparel</td>\\n\",\n              \"      <td>Topwear</td>\\n\",\n              \"      <td>Tshirts</td>\\n\",\n              \"      <td>Grey</td>\\n\",\n              \"      <td>Summer</td>\\n\",\n              \"      <td>2012</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Puma Men Grey T-shirt</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>...</th>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"      <td>...</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>44419</th>\\n\",\n              \"      <td>17036</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Footwear</td>\\n\",\n              \"      <td>Shoes</td>\\n\",\n              \"      <td>Casual Shoes</td>\\n\",\n              \"      <td>White</td>\\n\",\n              \"      <td>Summer</td>\\n\",\n              \"      <td>2013</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Gas Men Caddy Casual Shoe</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>44420</th>\\n\",\n              \"      <td>6461</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Footwear</td>\\n\",\n              \"      <td>Flip Flops</td>\\n\",\n              \"      <td>Flip Flops</td>\\n\",\n              \"      <td>Red</td>\\n\",\n              \"      <td>Summer</td>\\n\",\n              \"      <td>2011</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Lotto Men's Soccer Track Flip Flop</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>44421</th>\\n\",\n              \"      <td>18842</td>\\n\",\n              \"      <td>Men</td>\\n\",\n              \"      <td>Apparel</td>\\n\",\n              \"      <td>Topwear</td>\\n\",\n              \"      <td>Tshirts</td>\\n\",\n              \"      <td>Blue</td>\\n\",\n              \"      <td>Fall</td>\\n\",\n              \"      <td>2011</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Puma Men Graphic Stellar Blue Tshirt</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>44422</th>\\n\",\n              \"      <td>46694</td>\\n\",\n              \"      <td>Women</td>\\n\",\n              \"      <td>Personal Care</td>\\n\",\n              \"      <td>Fragrance</td>\\n\",\n              \"      <td>Perfume and Body Mist</td>\\n\",\n              \"      <td>Blue</td>\\n\",\n              \"      <td>Spring</td>\\n\",\n              \"      <td>2017</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Rasasi Women Blue Lady Perfume</td>\\n\",\n              \"    </tr>\\n\",\n              \"    <tr>\\n\",\n              \"      <th>44423</th>\\n\",\n              \"      <td>51623</td>\\n\",\n              \"      <td>Women</td>\\n\",\n              \"      <td>Accessories</td>\\n\",\n              \"      <td>Watches</td>\\n\",\n              \"      <td>Watches</td>\\n\",\n              \"      <td>Pink</td>\\n\",\n              \"      <td>Winter</td>\\n\",\n              \"      <td>2016</td>\\n\",\n              \"      <td>Casual</td>\\n\",\n              \"      <td>Fossil Women Pink Dial Chronograph Watch ES3050</td>\\n\",\n              \"    </tr>\\n\",\n              \"  </tbody>\\n\",\n              \"</table>\\n\",\n              \"<p>44077 rows × 10 columns</p>\\n\",\n              \"</div>\\n\",\n              \"      <button class=\\\"colab-df-convert\\\" onclick=\\\"convertToInteractive('df-6f4ac1df-0406-441f-8085-9b5b8a657a4c')\\\"\\n\",\n              \"              title=\\\"Convert this dataframe to an interactive table.\\\"\\n\",\n              \"              style=\\\"display:none;\\\">\\n\",\n              \"        \\n\",\n              \"  <svg xmlns=\\\"http://www.w3.org/2000/svg\\\" height=\\\"24px\\\"viewBox=\\\"0 0 24 24\\\"\\n\",\n              \"       width=\\\"24px\\\">\\n\",\n              \"    <path d=\\\"M0 0h24v24H0V0z\\\" fill=\\\"none\\\"/>\\n\",\n              \"    <path d=\\\"M18.56 5.44l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94zm-11 1L8.5 8.5l.94-2.06 2.06-.94-2.06-.94L8.5 2.5l-.94 2.06-2.06.94zm10 10l.94 2.06.94-2.06 2.06-.94-2.06-.94-.94-2.06-.94 2.06-2.06.94z\\\"/><path d=\\\"M17.41 7.96l-1.37-1.37c-.4-.4-.92-.59-1.43-.59-.52 0-1.04.2-1.43.59L10.3 9.45l-7.72 7.72c-.78.78-.78 2.05 0 2.83L4 21.41c.39.39.9.59 1.41.59.51 0 1.02-.2 1.41-.59l7.78-7.78 2.81-2.81c.8-.78.8-2.07 0-2.86zM5.41 20L4 18.59l7.72-7.72 1.47 1.35L5.41 20z\\\"/>\\n\",\n              \"  </svg>\\n\",\n              \"      </button>\\n\",\n              \"      \\n\",\n              \"  <style>\\n\",\n              \"    .colab-df-container {\\n\",\n              \"      display:flex;\\n\",\n              \"      flex-wrap:wrap;\\n\",\n              \"      gap: 12px;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .colab-df-convert {\\n\",\n              \"      background-color: #E8F0FE;\\n\",\n              \"      border: none;\\n\",\n              \"      border-radius: 50%;\\n\",\n              \"      cursor: pointer;\\n\",\n              \"      display: none;\\n\",\n              \"      fill: #1967D2;\\n\",\n              \"      height: 32px;\\n\",\n              \"      padding: 0 0 0 0;\\n\",\n              \"      width: 32px;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    .colab-df-convert:hover {\\n\",\n              \"      background-color: #E2EBFA;\\n\",\n              \"      box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\\n\",\n              \"      fill: #174EA6;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    [theme=dark] .colab-df-convert {\\n\",\n              \"      background-color: #3B4455;\\n\",\n              \"      fill: #D2E3FC;\\n\",\n              \"    }\\n\",\n              \"\\n\",\n              \"    [theme=dark] .colab-df-convert:hover {\\n\",\n              \"      background-color: #434B5C;\\n\",\n              \"      box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\\n\",\n              \"      filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\\n\",\n              \"      fill: #FFFFFF;\\n\",\n              \"    }\\n\",\n              \"  </style>\\n\",\n              \"\\n\",\n              \"      <script>\\n\",\n              \"        const buttonEl =\\n\",\n              \"          document.querySelector('#df-6f4ac1df-0406-441f-8085-9b5b8a657a4c button.colab-df-convert');\\n\",\n              \"        buttonEl.style.display =\\n\",\n              \"          google.colab.kernel.accessAllowed ? 'block' : 'none';\\n\",\n              \"\\n\",\n              \"        async function convertToInteractive(key) {\\n\",\n              \"          const element = document.querySelector('#df-6f4ac1df-0406-441f-8085-9b5b8a657a4c');\\n\",\n              \"          const dataTable =\\n\",\n              \"            await google.colab.kernel.invokeFunction('convertToInteractive',\\n\",\n              \"                                                     [key], {});\\n\",\n              \"          if (!dataTable) return;\\n\",\n              \"\\n\",\n              \"          const docLinkHtml = 'Like what you see? Visit the ' +\\n\",\n              \"            '<a target=\\\"_blank\\\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\\n\",\n              \"            + ' to learn more about interactive tables.';\\n\",\n              \"          element.innerHTML = '';\\n\",\n              \"          dataTable['output_type'] = 'display_data';\\n\",\n              \"          await google.colab.output.renderOutput(dataTable, element);\\n\",\n              \"          const docLink = document.createElement('div');\\n\",\n              \"          docLink.innerHTML = docLinkHtml;\\n\",\n              \"          element.appendChild(docLink);\\n\",\n              \"        }\\n\",\n              \"      </script>\\n\",\n              \"    </div>\\n\",\n              \"  </div>\\n\",\n              \"  \"\n            ],\n            \"text/plain\": [\n              \"          id gender masterCategory subCategory            articleType  \\\\\\n\",\n              \"0      15970    Men        Apparel     Topwear                 Shirts   \\n\",\n              \"1      39386    Men        Apparel  Bottomwear                  Jeans   \\n\",\n              \"2      59263  Women    Accessories     Watches                Watches   \\n\",\n              \"3      21379    Men        Apparel  Bottomwear            Track Pants   \\n\",\n              \"4      53759    Men        Apparel     Topwear                Tshirts   \\n\",\n              \"...      ...    ...            ...         ...                    ...   \\n\",\n              \"44419  17036    Men       Footwear       Shoes           Casual Shoes   \\n\",\n              \"44420   6461    Men       Footwear  Flip Flops             Flip Flops   \\n\",\n              \"44421  18842    Men        Apparel     Topwear                Tshirts   \\n\",\n              \"44422  46694  Women  Personal Care   Fragrance  Perfume and Body Mist   \\n\",\n              \"44423  51623  Women    Accessories     Watches                Watches   \\n\",\n              \"\\n\",\n              \"      baseColour  season  year   usage  \\\\\\n\",\n              \"0      Navy Blue    Fall  2011  Casual   \\n\",\n              \"1           Blue  Summer  2012  Casual   \\n\",\n              \"2         Silver  Winter  2016  Casual   \\n\",\n              \"3          Black    Fall  2011  Casual   \\n\",\n              \"4           Grey  Summer  2012  Casual   \\n\",\n              \"...          ...     ...   ...     ...   \\n\",\n              \"44419      White  Summer  2013  Casual   \\n\",\n              \"44420        Red  Summer  2011  Casual   \\n\",\n              \"44421       Blue    Fall  2011  Casual   \\n\",\n              \"44422       Blue  Spring  2017  Casual   \\n\",\n              \"44423       Pink  Winter  2016  Casual   \\n\",\n              \"\\n\",\n              \"                                    productDisplayName  \\n\",\n              \"0                     Turtle Check Men Navy Blue Shirt  \\n\",\n              \"1                   Peter England Men Party Blue Jeans  \\n\",\n              \"2                             Titan Women Silver Watch  \\n\",\n              \"3        Manchester United Men Solid Black Track Pants  \\n\",\n              \"4                                Puma Men Grey T-shirt  \\n\",\n              \"...                                                ...  \\n\",\n              \"44419                        Gas Men Caddy Casual Shoe  \\n\",\n              \"44420               Lotto Men's Soccer Track Flip Flop  \\n\",\n              \"44421             Puma Men Graphic Stellar Blue Tshirt  \\n\",\n              \"44422                   Rasasi Women Blue Lady Perfume  \\n\",\n              \"44423  Fossil Women Pink Dial Chronograph Watch ES3050  \\n\",\n              \"\\n\",\n              \"[44077 rows x 10 columns]\"\n            ]\n          },\n          \"metadata\": {},\n          \"execution_count\": 6\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"import os\\n\",\n        \"\\n\",\n        \"def get_product_docs(max_num: int = MAX_NUM_DOCS):\\n\",\n        \"    da = DocumentArray()\\n\",\n        \"    for index, row in df.iterrows():\\n\",\n        \"        doc_id = row.pop('id')\\n\",\n        \"        doc_uri = f'/content/data/data/{doc_id}.jpg'\\n\",\n        \"        if not os.path.exists(doc_uri):\\n\",\n        \"            continue\\n\",\n        \"\\n\",\n        \"        doc = Document(id=str(doc_id), uri=doc_uri, tags=dict(row))\\n\",\n        \"        da.append(doc)\\n\",\n        \"        if len(da) == max_num:\\n\",\n        \"            break\\n\",\n        \"    \\n\",\n        \"    return da\"\n      ],\n      \"metadata\": {\n        \"id\": \"ATITPHWAvHzV\"\n      },\n      \"execution_count\": 34,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"docs = get_product_docs(500)\\n\",\n        \"print(len(docs)) # should be 500\\n\",\n        \"docs.plot_image_sprites()\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 337\n        },\n        \"id\": \"Ilbu6gwMvUEO\",\n        \"outputId\": \"252e86dc-be9d-4078-d3a7-5b9d6872abf8\"\n      },\n      \"execution_count\": 35,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"500\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAAS4AAAEuCAYAAAAwQP9DAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOy9d3Sdx3Xu/Zu3nV7QCwEQAMEGgF2iSFFUJdWrZVmSJffEJc127s1KuZ+v7STXjpXPjp1i59qx47hKtlzUZXWJIiWxSyTBCgJE78Dp57xt7h/nACAIgKTKt+7ntbjXAijhvDOz956Z592z55k5QkrJBbkgF+SC/D6J8n9bgQtyQS7IBXm7cgG4LsgFuSC/d3IBuC7IBbkgv3dyAbguyAW5IL93cgG4LsgFuSC/d3IBuC7IBbkgv3einePzGVwJKSVDvb288sJzXHbNFiqrFyCEeM+UkdIqNKkhXQehTOKqBBSEUM54XiJdyQsvPY9t2Vyx+Qq8fh/AlF7vlO7xbuySUuK6Lo89/STJWIK777oLTdPOWaeUkv27d7J3zx6WtzSz8bIr3lP/XpDzF8dxyGQy/O7pp9B0jWuu3kogGHjP+0NKSWfnKbq7e9i0aSOKoszfhpS4SGzL4be/eBCvz8cNt78PTXFxUVHfA92klEgp6e7q5s3dO9ANnWtvuuPsehXK9fcP4jgONTXVgECIaQB5h5rNW+xcwDVDMdd1+dX3vsHapjAndr1Ixa0fRAj1nak0pwhcJ0si1oFQVUKhGqSio6DOa4HjWDzy4H/gI0cooLPx0qsRyrndNAlo/18Ag5SSN/ft5tGf/wCP4lJZHOLq624+Z+dJKXn+1z/ipace5aeuxnceepyly1vec/3+/yYnT57ElZKKinIC/gCQ75f/m6CtKAq5bIoXn3iIdDpF87LFLFrcwvyYInEch45Tp+jp6aGsrJSW5c0IxLzTT0rJ4bY2/vUrXyIRT9Bx/P18+OOfQEo5j+35vz38w+9Tq4zx8KPbWLR0Gc0tze+N0VN6ubz47G9Z4B0jM+HS27OO2rr6szwP+/e/xSM/+h6maXH5Dbdy7Y03IITyTgHrnHLewAUSKV2G+weoW1PEz373COuuuxOf13fWznk7kk72MdD5c5zkUQRBhv1N1Cz5EB5vpPDEmY0IhKrwuQ/djp0cJ1pdyrwj6wwZHRllsL+fuvqFhEKh8y53XiIECyuK+Opn7kBVXdzqxvOsXrK+Psj6uy6iJ+WhvKTkXatyZsQphJgF2u8WxCfLnznhzqe+js5OfvCj/8Lj9VJVVUntghq2XHX1uwYticTMWSSTKYqLo+THzrQvzhn94hLyqnz98x8iFR/HUxadsw8nbU9nMnz7u9/jd889R19fH6FwiL//4hfZcuWVIMU8/S/pOnKINZURSpbW8vJrO/jQxz4+r26T0VAqMUEgGCdCgrYDB2huaZmaGXl1JOlcBkPT0VTtvOw9XadsJs3BvbtZfVU93YO97Nj+CnfX1ZP331z1uBw7uI+1C4ooCniZGO17xyud85XzBi7LyjAyspeVa0spqq9l4ZJTdJx8mCWL70DXg7wXyKVpKqpznJxdRCJxDCPZxai/lapFlyKEZ8azknwnpjM5uofGqY0IXn51O5tDtRRFQngMfd7OmpgYJ+D3U9/YQFdHB8taWmcNLCklqVQa13UJhYLAmUN/9mCQSJBgZrO4vhL2nkpR4Ulz+MDTXHp9mJqKCjT9DL3kZJ0SAVSXREgNONRftYVoWUV+AAjyL4d3KLFYjGeeeQHHcUilkoyPjxONFlFdVY1lW2iaxsK6WppblqMoCiAxsxmcwnLd6/XPqO9Mux3H4YknnmD79h24UhKJRAgGgqxbt47NmzfNWWZSUqkUQijccuNNnOw4yURs4r2JtKSk7/Bx/vJLX+Mjt2xl8/tuwBUquqriD4bm9esUCLtgOgqx0SRYOXQJrmujKHMv+Q8fOcL3fvBDQuEQd9x6Ow//5tf83Ve/Rm3NApY2LWbu+SG4fMtWnj+2G4HLn396ftAqPI5tZrnkohZKzAGuFQqlSxqQjoNQJ1c+Ele6PPfGy6TiaW695nos08LQdTxeD6qSf25WO1Lmx6GU5NIJEiODVNdfxWBKMJyII3Mx8ITmXWFdtnEjnc8N4NoWF2+4JD+O3uU68Wxy3sCVy7WTGn+MhSv9DMoYtauqGB58gkWNVxSA692L7VikUxpe/xhevYiJRIpoSS1z7SG4ruS5V3fzgwd/iZoa4LZrLuHBlw/xfNu3aV7SyB9/+G5UbW4nnzx+gtKyMmoXLqS3u5vq2loikWj+w4KTR0ZG+W9//tccO97GvffcS2l5OZZpsmbNKlasaJlzgEkJ6VSKf//mP/PCjleZcCQlhsZYIsEvfvs4d970Pu79w0+h68Z0GSTYOexMAqwsoeoGXN2LHi7FSoyg+kIommdKLyllviFxmrJnEdM0efnFF/nWN79JKBSitKwMy7Q42XESn9dHfX0DE7EJopEA3/zWNykqKiIRj/GDf/5bjpzsJFRSzn33f4Ldu9+ktXU5F120Hk3TT7NZ0t/Xz45XXsMf8PDGrr2sWrWSw21H6O/v49JLN6Cq6ow38Om+q6uro6SkmMefehIhJZs3b84vE0+z7WxRI4DrugXAnezC/CvmxMMPUrVvB7/c9TL7//XrHAhXEiop5eaP3Mc1111BwOMD5KxoO5PJMDA0hOu6qHVrSY4OMtx2nPCCNNXVNXi9vinfT+ri8XgoryijsbER0zbx+X040uVEe0cBuE4fJ5JsNkd37zC6buAW1ZAdaienBBkdS+O4JuWl0Vm2Otk0ic4j1NUtwhG1LF/YimFDOh4nUBQC8v1iWzaP/uzXbNvzBo8/9jjjQyNEa8q5estW7rv+dnweTwG4p70sKQQD6RGUXAfd2QCP7pvANkO8/MZONrdEaVp/Oxih0wzJlxkcGuet9gG6e4cpifg4+OwOKk6Msnn9akpLo1MAJqbG8KxhOkvOtYo7b+DyexeiJosQvg6GYn5QszTEKjFk6NyFz1M0vYRA8Ursib04rklRxSpUrxdQZgWp8USSB3/7OMmMie1G+P6zR1hWv4C+vn4OuDaDwyNUV1XMamNsdJRjbW30R6P09PRwsrOD/Xv38JnPfp5AMA/AUkra29vZ9uorxOPjfP2fvkkkEsVxJKFwiC9/8W/YuHED4XCo0BmFQQzE0mle3rEL6SgEvDrxXJZIsAyvJvjJ48+weN16Nl5yyZQ+0rWJndxDYs+vKa5vpWz5NZS3ariWhZOJYZlJjEgVQvOCkDzz5OP09ZxC1zQi4XykKxQVv8+HP+DDlQJFqISjRSxpWUEikeDAwTa6ujuxbQfD8BCNRnEcB9uyOH7iKK50ufeeP8Pr9eb7OhAgVLWE8bdOsv/4QVTj1+zYsQNdVfnqV/+B9evXz/Cpoij09naTyaRIxsbZv2cn7R1d6MoGXtvxOkKBpqYmKipm90cwGGDD+vX89rHHCQYCrGhumT/KLEy2yYnc09OD67ocOHCATRs3krVMUskUdXV1CF2juGERm1avpH/nTsy+QeoSsKO3j57PH6Tjzz/FXXfcSnVV9axo27QsEskM4UgEVTGIdb5Fd1cP626tJJPNFIBrpixpauLb//QN/uqLX+L1N3aiqgqbVq7kyssuO8OEvO6Hj3Xj9UXQhcG6m+8nPT6EHioimbFIpXIURWx0XeP0GN8x0wyfOkztmmvxqpJMKsWpzn0sXdgMp0VCjuOQisWxxpLseOZFoiVFdA72sb/tMDt2vsHHb3k/l1y0Hl3TpyeVACltMuP9oKi4qpef/nY7mq6hK5Ljh4+yaP3MHpFInntpB9/4lx8yPjaGR3HRdI2MPAFs45cLKvna3/53amsqp7rvTLEySTTDj+XaYKYZGx/CclVq6xaddZWhfulLX5r3Q2DqQxEfArsTEcmgK2F0zzBF2Tp8jRtANWa8RaXj4koby8ygqNNvZ1ea2KkUQhEIZTZmCuHgmCl6u7YRj2fQZIzk8F40XykeXyWTkZcQgmQiyfbXdzM42I/mDWCZGUYnYjiZOHdeVMmyxU14w9M5olQqxe433uBbDzzA4QOH2Ld3LzteeomO9g4mxsY5dvQIZWVleLxevF4vPp8fvz/E8PAwyWSCiYkxNEXBypk88dTTPPPsM4RCQcrLy/H5fFP2b3vlRQ4dOoSdSpOzbFxH4rWSqJokVBJm3eoWauoa8pNFQvehPbzwmx+TxUf1ys14omWg5IE6l06S7jnMyBu/ItC4FlX38IPvfodXX3ye9uNHOXroLQ6+tZ9Db+3nwL69HNi/l9e3v8rzzz1POp1i/cZN9PT28cAD/0hLazPDwyOFaAZ0TceyLEzLxLYstm7dysKFdXi9XoRQqKhawNPPvYCdy3DyZAfYJkvqyslmTRY1NeLz+ZHkc1q6oVNbV8vI8AijoyMkYzHqqhdw4lQPjz/2Ox566Gfs2buXTZsuJRQKzcitHTrUxjPPPg9Irtuyha6ebkqLS/B4PFN9DQLHsUknk5w43IbjOIwPDfLGS89x5K39tDa3cvzQQfY8+GPa9u9j0crVBIMhSltbqLr2GhZsvozBdAJfXx+NZVEUS5A6+BYTBw9il5ZQuaAqn0gWAldKkl1vARLX48e0TNr37SeLQ3F9JUGfH58vOCuXpygKRdEowyNjHD58BCEEH77vg6xsbZ1zo+F4ez8TExNEi4vw6DqeUBgpVMycic/vJRz0oCgCkfcyAonIxbCGewhX1IPioNgW/V3HqV62AqEaU9Nc0zRWrVrF+osvxnUdYuMxsokUTfX1vPXmm+x6YyddHR20NDfj852eBlAw4wOkEzF+9eQ2hKohFBUFm7tv2ETZwhbEafMZ4At/+wBHjx0HRcVExXQFqqIjXZfBgW5uuXELpaXFU/3d13mEscFeUtk0B3f/jr3P/5KhoVMEi8rY9tzDvLntEcZTOZa1rEUI8eU5UYm3AVxOfyfDY68S1yZQfCkcmSGXEBTXb0aonpnAZZuM/uInDD/+ML2nDnDs8DZ633oVZfs+Uo8+hLduEUppaeFlYiJRka7FUNeDjHX+Bn+gmJLydWQy7QwO9DHavYfSqqVonoqpQeBRHC4rT2CpQTrGLSqqa4kUFXPjli1cXa9R0tCKMAJTOu3fu5c//9M/wczkUFQFVbhYZgZwKSoqITYxwQvPPoM3EGBZczN+v4+LL17DTTfeQkPDYiSCZDJBJBrFzOUYGBjg+RdeoK3tCDdcfx26nu/QSnWcay9dRU8Wurt7ECgMOwqNK1byhY/fxopli1G8YYRQGO87xUtf/yNa6kL4alZR3dSKanhBKNhWjsTgKbp3P0PnwV3UbrwDzePjxWefZWiwH1XVClGoQNc0cqaNpimksxbxeJKWlSu5ZNNmJiYmKC+voK3tMF1dp3AcByHANHNkMhlyuSyqqrJp0yaWL1+GYeSXsX6/n2AwwPLlSxkfOElrQwWlUR+ZiX4O7Hud8bFR0HyUlpaiaRq1tbVs2bqFjZduYnh4lInYBLFknMTEGMK1mRgZobOjg9Vr1xGJ5DdbLMviZw/+jEw2xYrWFq66/Eq6u7t59fXXKCstJTIJcgIUoWB4PJRVVtPZfownv/dt1CNvIoYG2NPWhp3LUK5LKlasZvnadaiqiqqqBAIBorW11G7cSGDdWlYsX05l8yKab7mWqs0bGY2PEY/FqaqqnsrvZbvfxFtSi6v50XWVdCpGeWkR9TVlBMwR9HAlopArmoygXNdhIhZj+xs7aTvShlAEu/fuo7W5mZqqqilqjyhwBA49/xuOHTuJiUplVWUhulA4fOQoPmFhWWki0cJyUYIrwEpPIGwVESpDOBLXskmOD1DSuLQwL6ajruLiEpqamtiwYQM33ngdlRUVnDreTkP1Ar7wl39BRVkJHR0nqayoxOsrUIikRGT76e8f5rmdR1FUDaUAmnfeei2hkioUdTrNAfDM0y/Q0XECoWiomooUAtdxkLaFgcM1l62jurZ2yvZwURnxwU5+8tPvkuw5iGmmGR3sRqoqw+Mj+HweLr36DkKh4rMC13kvFW01R3e8D2/QwJFZFCQen4tjOSh6YSFXoEzE97xOx2M/wb94Bc6p47TvPEJlicpgUyu+wVOI//d/UfrlB/AsWABSQWBz6sSvOPjaf+L3uFQ1XUd53U0M9ryBKkxKGq5FD9QWaA4uoGLbJuMjQ7x1vBOhFzM0GsPKJPjx4YOs/vhmKqU7I5u+as0a7rnnfl7f9grxRBxVFaiKIBlPks12Eo1EWLV2HTfcfNOUk1VVpbKqlHvuvYMbb9rKQP8QEjh16hSWZeHz+aioKJua7AA6Lhmh0jEQRy+uRtMUwlJnqHeQ7sEJamozGOT1UjWVxku2UrtpK7rHh6LmJ06etQaekloSySRmuC4PaIWJYlouYAMSRZCP7FwXK+5gWg6arqMoGoqiUFZWRll5Gfv370dVVEzTIuD3MTIyiitB1zXGx8c5eOAAd955x5Qdqqpy4w034jgOFRU1dHacpK+vm65T3ZR6oyQsndrauunJWNBtUVMj//PLX+TFF14kkYgxMjLG4OAQoVCIispyioqKptrIZrOFDZYM0WgURVVYs3o1O/fs5tv/+9/54898hppJrmAh8pJS0rJmHVgmSnwMxTBYsGIdmseHlU3jD0dnceY0TaOisoqSAsguymbo6Oigvb2DkZExFtY1TA0UgYtEYTyewl8cAWFQv2od2dQE6H5kJj4r5yalpKunj8/9xV8wOj7BDddeS0VpKT/82c/57g/+k9UrWvH7/VPPjg0NkOs/Qm3tJorCpbiuBEXiOJKAx+Docz+nZMFCahd+PB9tCRdQEKaLqgVwXAdsG0tR8uVwULEBfUZ0pygK5eXllJeX0VDfyL333ENvby8n29vp7+unqLh4CoALHYmdGuEXT71KJpMBJiPJCMLwo6jaDJvjI4Pc2RphoreKtr4Uru0gVHBcF1yXT968BvqOABsK8JeXupYNfK6xlSMHX0VIB0X10LL6iql6VVU/5y7/+QNXxsTjeEmlEiiqQsDrwcnmKKT0pjBCYtP3o/8klzV5bdt2VmxYiW7rhJZcRNuxN8gOx9iUG8X76ot4PnBfvrwQuG4JqtePFDkq67egh+spbfoAuhakonYTmu4rbCu7+U7RNIxgCeHyDF09cRRNxeP1kM4adCdcVjkOYmrHDgzD4OY7budw22HGJ2I4joumAEJDQaG4uJSb7rgDw5jevZwcAKoqKCoKE43md6MaG2uQUqJpGpo27UIBJDMm6ZzDcP9JRobHEAXKhmOnCQTuQlM9gAoIwuU1XPzhv0YAmcHjKKqK69goqo5QNYxAkPWf+BqoBoYvn3+75X13cfnV1+UTvGYGy7SIxWL4/X4cx8EybRRFsGr1aoQQRKNRurpOEY/HqKqsoLunl94+c2pQApSXVxAOR3Bdd4bdkJ/011xzFVJeSTabJZfL4fF4ZiyPz/RXMBjglltvnuL+OY6DpmkzEuiQB24pwbYcjh07zhWbNhMMhVi2dCk73nidoaEhFlRVz6h7MnHfuv7SKfrFlB6BAHPJ5DOGkU9Ke7x+6hsWUV5RheM4RCIR1MldOSmJllaS08uxXQcFgW4YIIMYhoGhlaJoM5dLjuuyc/ce2k92ctG6tfzJpz+N49g89cLz7Nq1m2QyNQVcAN0njpKMxSlfGiQcieLKfEjluC7hSBSztIKJgd5CdDYNkjkzh2mZeBUF27aRrktyeIDMxBhGUcWc52Aml9qqphAIhqmt0ykuKce2Lfw+P6FCnlbKfFv9g0M88fwuMq5KSUkJsXgC25V0HD/OgobW6VSalPS07cdjaESraqDvKNJxcGwLTdVQVYWlq9Zij3eTTaXxBmaSd/3+EGsuvn7WeDtfOQ/gytMOhrRuEsYows2TQc1cDsfwYaOhn+YxF5UFf/rfKdFtllourq6xLJ7DE/CzxLoLU5f40jbeomi+UxQVgULd4quJRMK0vfkUmr4ARVGpb3ofQpFIXARKAYTznlM9AYIllWRGt+PE48QScZAOQgG/PYErlJn9KKC+qYG/+dsvMjo8PDXoJaCpglA4wsLGxrPu001OgMkk9lziLaqiY/d2wqaCT/MiXQtVqAhfhHTXUdKLGwmJ0+tTcV2H4fEkUVclGPIghYKr5P3sCc/kcm24dOMMfaY4WKeFl/K0zQIE3H//faxcuYJJsMq3m69DVTV8Ph9Lly6ZWu7OZ7vP55sTsM5WRlGUWYAFBUKz47Jy5UoWZzKkUymEoqAIwQ3XXcfa1Wuorq6et613SlCdLDNpy2mf5PVCITk6wLd+9EMOd3Sh6xqGR8dj6HzyjutZsXwxevkZFArXpavrFJdftpEv/Y//h/KyUrK5HA/8/d/R09uHaVszOG6+cAQ9WESkYgGqrmGaLrqmYJkufr+PaHklUtHzZQo6CcAorsY0B7FzSZAujpNjxZbb8JdWgzj3VBaCeftQkAcvRw3hSIkUGsePHSMQiuJYFiKRgsKLDfI7uUd2vUYo7EdqHhzbREoXIRRc4aBpKobhIZnOos4zrt4N7eU8Iy5BSflaFI/CkfZHEUoOV4eK4kbUGVv7oCoq4eUthOep6czdwdPD2kjVJVxWtQYHHZAIhQL7dvbAV4SKMEIsLwtwZGCQ/q52dI9BVWMjOw93cfVdMykaotBWTW0tNXU108qcpsO7pcxJAZ5gMWWGF9NfQntc4qQGIFBNy4IyyquqcRx1cnMsX0ZKcjmTf/yX7+Hz+QiFi1E0Fdu2KSst5pOf+lQBUOafwDOtPMO/CMLhMJdddtksGsHcdZyrjbcnZysbDAa59potsxLdhmJQW1Pzjtt8NzoJBCdGLF59bRuDYxOkEik8Xg/LGupoiBrIOTaVdF3nLz732RnLG6/Hw6qWVlqXLZ8igU7K4paVHN1WierzkEimyNk6ilDIZtIY0kJDsvrKrZPKQgHAhKKxY/cbjI30sbJ1FWYuy6JVa/ALdc45Mqd185oukUIS8pfQUO7nyKCFrmn4AwHWNDfRtG4TrmpMtSKlJBcfo6zlYnZ9/1lAIF2JVBwUKXCli+oPY6HmI9b3mMt1HsCVNzYUWow/sIjtL8WprKphrH+Q1iu3YExt2QqUOblNEsu0QLpoupHfUZzjOUVREFICeecIzn1cINSwknXXgah7E2vz5SyoqWE0mWJBaTGKJ8RMyqiY+kdIsF0XRZEoYuZSby79J4mltm2jqmpe13l4JlqoBHf5Zm6+I0Q8Ps661sW8ebgDgGDzZsILqmYNnlQqxSWbryaTyeE6FmYuh2F4KS0ryec/5tHrtP+ZmjRz+XYSsCaXbpO8p7kioXcjU3wj28Z2KeQRlVnR0XycrjM/O9OGd6ub4zh5bpaqFJaGs+t0pSRUs5j3f+A+hBAsXNjA8ePHUBVBomQplQuXzK3rPH4/PZUwFR0rCrWNixg4uZe+tEpRcTG6rjM2Ns6yYg0PECmefQrE8HqpW7qc3rERsqqH7qE+1pdVzGnH2xaR17e8+VI+9KnP0nbkOAJBIBDiskvWUdm0fIa9iqpy0bW34alexKf/8KNkc1nMrAmAbhhomkZdYyNlQc8s9fL1OEhH4tg2qBqqpuXB+Rz8rRmVnOVnSlzXmfqRk//tuNJ1XelKV84n8XhMfuj+u+UnPnKX3Ldnl3Td+Z99u+K6rrQtWz74k5/Ij919p/zJD74rLcuUZ1FHuq4rY+PD8tCuJ+Wp9r3n1Md18zb+/Mffl1uu3CC//sBXpGmaUr6HdkzKyMiI/MUvfiEfeugh+Ytf/EIODAycVS/HcWRX+0G5+5XHZTweO6strutK27ZlX1+//PSn/kh+9Sv/IBOJ5HveH5Zlyb/+q/8mb7z+Svmtb35d2rY9q41Jn5754ziOdBxnzs/erV7ZbFb+7d99SV66ab388pe/cM46HceRw8PD8s8+9xl51/tvk0ePHjlvO871827syGSy8p++9YC89ZYr5Y9//B9T/nqvxHVdaZqm/OF//Ze8++73y9/8+lfn1NtxHNnX1yfvvvcuef/998i+vp5zj0XTlo89+ph8/113yn/+5jfkyPBwvszMcvNi03kn50eTKXYdOcaRU7001lWzqaWZYp/3nEdRujtPcs2KSmpKQ8SGTgHrzrfJ8xKJ5NRbr/GJWzez7+ghTNNC9enzaiUljA12MXp0NydNhcqaJRi6/6y7GFK6pMc6+B+fvp1XXt9HJptACxW95ycZjp84wSc+8YlC3k3lk5/8JP/wD/8wb7Rx9EgbbS89TIgMz3f3cds9nzirHYoieOXJR7i8VGBYI3QcOkDr+vW8p3G8cLl54zIuqjDxlmoI8jti55JYLIFtO/T1D9DasnQGsfe9ECkdbt/YxNYV99HWk8J1QVHOvnn1yK8f4vqWMroCFjteepLFi5fM/3BBMqkU+1/ZgcfnZ+Wmi9HOssx/uyIEZLNpnNF2/uKea9hzYn/+j+/FEanTJJVKse3pR/jkrZt4/fVXuO3WW0GZ/zIFIQTbt70M6XFu23op4wNdVFZWcTa7M2aal3/9Ez6wtBqZHiUxPEJxSQkIh/NZCJ51RE2eBxyNx/nPJ5/jyX2H6E3m2Nc3zCM7dmI6Nnl6whxlpcSVEiVisPKiZbQ2N7J49XsLWpCfjH/wyY+ytK6Mu95/Kx6PcdbnpXQxfEGiZdUsXrKY0aFuMtn8mcT5clxCwD33fpSVDVV85lOfwecPzkiITb4FHMdhaGiIZDKZr+88D5pOlleEwOcP4PN6URSFN996a+qzucpUVFawsK6a5mVLWbx4Me5pdc0lritZvnwZHlUlFPZTWRrJJ+bm0eednJNVhMLqiy9l7fJGmhvLcLIpkPm25VmyiIcPtxOOBCkqKmJsPPau841nimoYLFraSnNVOddcdgn5cTt/K0IISkqLyaTTNNeH2bf79fyu+XxOyX/I6089S+vG9Rheg2N73pzTh1JKLNMkk0njODaWNXmd09lFSggEgtx7x60sqS7j+i3XIl37vMq+XVm7tIE1i+uQmAwOjpw1QJFIpJWhdUGI5oYSThw/jOVauPOORYGu+th600IcgykAACAASURBVA24fpWK0jBl1ZWAi3ueIH8OAqr7JRCMpjI8tXMfkZIiNq1cQTKZZDw+wYblS9EVBc64vsKVkuFsgseP7eG/9r5MsTtG+1g/Pz/cQUa4NJZUoanv7jocKSWWY9Pb20X34TZ0a4i+vhR2wgYB3lA+x3V6EhwJfX2dnOo6gWV48IZLSaVT9PacwOf14PNHZuZiyIPvYMymZ8xlqLeT8cAyLL0Yx5H4zsDI/r5ePvaRj7Jr5y7i8RhHjx2jrq5uarfuXHmazlOnaDt8BL8/gOtK/uxP/5RVq1bOKDs5EDLZNJlMOj9kM3ECdauwHYdcLovP653RlutKpHRwXYcTB19n+MQhykrCROpCJHMSfyg0ndyVcKq3nx/99GG2v7qNA4eOcfzIYSzHprKiYl4bpCuxpIm96zE48Cy5kZN4R0/iee1nCHMIopUIb3SOnBZTnLhQ0E9HRxcCSTQamfWsbdtMdPeS7u0jOz5BNpEgl0ygCEEmHqe7s5NcNkcgHJ7eiJDQHR/jmeNv8Ur7YXZ2n+REKklnIoPP46XIH5wz/9a1azf+rkGKFoRZtKIFR4ao8UTxlkby4/1MO0QejF75zWMsu3gtE4NDpBNJahYvmu0rKdn20pPs2LGNsdFxxidieDwGPp9/zkhTSgmuJNU3yL6f/pKxxElKIvDijj0Mt3XhlxrByorzus5pPpEFMlPcsvldTx9HpBe7sop9MejIWpSVl1Di8czylSMle0+e4unnH8cIezg0keK3L+5mrKSK2uISwl7PjLE7+ZNKpTjSdpB0Xx/h8mqywWICPh1V8zFpxrsgoOYJkWXhAK2NdXSOTXCs+xTjyQRrGxoxlHwafdZWoYQfHtzOv+5/lhWBGr4ymqbUG2A4M8KjT/wni8tqWLmgcS7vFTQ+7R0y73liSefRY4wPD5JSApiiAdew6R8aYsTO0VJcfMZxBnAck4nRfjTDR+OiFjKJGKqw8XkMcskRrHApuuFnEvAk0DOaZTSroQbK0JvfhxEwSJgwlrAwNI2wb/pU/o5tr7C0qYF0NsvTTz9Nb28fjz/+BH/1V3/J4sWLp7hCc/GfpJSMjo2hqipV1QsIhSNcfvnlc/aK6zqMjgyje31E6lZgVDWQSMYYi43iKyrB5/Xg9Z52r5ULvd0HSQ9tI2yM0LJiAR6vjqF24SQ6GDxeTEXT+1EKy4GRoUGOHz5MKp0iGi1jz549GJrka1/7X6xevWLOS+WkkIjdj6E89VWkz4CS5YzaPopiHcjnvgPBali78LTem6ZxtHf00N7Ry03Xb6K6uoKTnQPU18+2+63Hn+HVb3+PzEA3wpUITUEiUHQdV0r8ldUsu34rpZ/+OEpht9vB5Yd7XyaWTuKaNpmBLNn0Saz+GC/3HuMbN30Uv+eMm0dcl66RURIHTxCsq2Kkq5fytOC5hx7m5j/+FJ6K0tnKSUiOj2M5Nl6/j0hZKf5Qbjohf5q/ent6mIjnWLRoGaqiY+VyjI+N4/cHCxSN2fdxmbksr/7Ld0g8txu5pp5EayXukELf8b2M7T3ITV/7e0KRSXKvmPbwZKQrJVbWxBv0n+H/abGl5M2RcV4YGkVbsJCXcgZG6xoOjA6zOJZkcSTCmeFGT2yMrzz7O4p8Hq5c3cLPj47QuTjKd158kePxGF+74XpCHi+TMzpnmjz73AvYjkZKjeKtX824Hmbs5AD93UNUVNexasVssD9Tzgpck2xXj6Zz/fq1PPjqToYnxvH7AjRVlaIWdtvOfAk7wPj4GN6cZL/TQ06RDKZMmgLF2OOjJM3UrBAymUpx4vgJbNehd3CIWDJJNBymrqICw+OhvqGegNc35WnTtMjZFsXRUsqEQiKbRtVUcpk0JBLEJgbx+eqnumby6Eg45CfW10dyYoS2I8eoranGyqZQi6JIx5pphyMZS9m0dw+xfEkNmuFBVwSucDjaM0Kxv5SwLw/eruPS0dFBKp1GCIFrWwWAGeUH3/8BH/nIR1i8ZDGGYczg9MD0W768rAyvz8f69RdhWTY1NfPdMCvAcTkec0kYXkoKhGCPmMC2bSzLnAIuANvNMta3B8Uco6Q8jFLiohkehCoRuChOgmluCCxfupRbb7mOzlPdvHXoCC3NTXR2dvHFL/4dzc3LuWbLlaxds4rS0tLTqCQCC4GYmOCN5s9iLF4OoTCJZ7sJDAxi9nYQXGVDgbwppGAiFqd3YIS3Dh5jbDzFgQNtDI/EePaFPfgMSVlZKVVV01FebGgYZ2yMQCKJpgg0ICNdhKLhegyUVJrs+ATStEHPb8FnckkUO4M1OESxL4iq6NhmktGT7dh+nZF0jDpP+XRfAEhB7sAx3LoaQq0rEbbFSN8pCIfo7xulvqJsVo/kMhlOHjlGKBql9/hJhvv6cR2H8roaQkXR6TFl27z43O+IlpQxMjBMWSSCpzjCiRMncKSkuLiE4qJiVPW0l4OAkf1tDJ3owCn2EfDojI1mCWoe0qMDuIbBiX27WHXF1vzYsywe/8K/IV0bIZV8jjaVRLguXn8AVdPzsYamcMlHb6ViaUOhGUG5myE60EG2vhVNF1gBDzeHKrisSJkjryTJ5Ezs+CAx6cUsaSRX4kNjhA+EbW6vNNAVURjveTv6B4c4erydkkgJkUgE4a3ClpCaiDPuOCiGF3iXwCWnwh3QNANHg8uXLMHv9/LmiVMsrqrAM8fupWrbfLisjN7jGq9LBdexKPcE0BX4YHU5JUpuxvNHjx3jO//+v7n6yitRBCxbuhR/IMjgwAADAwN09/bzo5/+jA9+8B5WFi5NE4pLVCY5fOAE0epaNNcl6zoMD/SwrCFMcWg2i1pKiXRdFKFw+OhRDMOgp68fnyHwen1I3BkcK9uWZB2VSDj/llIAV0IqaxPy66TdSZKgQCgKN9x8CxtGL+WZZ3/HsSNHqawoRwiV7q4u/vGBB1i1ahWXbd7MqlWr8rm40ygMUkpy2RyXXHwxPp+fhobKeekKjuOAEOgDnVy0bAlS2ggURpM2ilBRFHXGksO0s5w8cYDxvhOsvXglZVWVSFXDlRqp+AQKDlYui+7zo6CgagqGpmDlcvR0dfPh++/F6/Xwb//272zf8QY7d+1meXMr999/D5duWJcnmkqwKhtwLrmTjd5RRg98H8tJYqVtzBU3kalqJHD6WBHw7Eu7+NZ/PEpsuBNVC/LzX/qxrTSayPD4bx/kvvvu4nOf+zS+AuFXESAcF11VUUSeiuyRAicaZaKyDM0VmLkcQsjJAYyaGubesJcnFIP+kWGw82c6r2heyZLyMP7YCBRNA5cAsmaWXH0NiZd20360E8PrxR8KIDSJbmc5c4khgYM7d/OVP/k82XSWV37+a5I42Lkct334Pt7/Z5/Oc5kKDSwsKkZOJMiUhohUVWLZNuFIhIG+Prq7uti1azdIwT333k1DQx5UIouqWbxlFbvaOjAsF0P3gJkhXqJxxdY1REqKp/VxXcaOdyFMCyFUbGmTK9LxpBxGhk8SLSnDLYzlbCw5ZbmCYKGaY8WCcvaphZhMVSnNjlEqs7MHolBYEgnwpTV1/NOgxsPHurioqISeiQmaIx6ay4oxdE+B3OoCAt3ILx2379jOxRddTHl5OZqmEAh4yGSzuI6FbdszaCRzydk/lS4IhXg6yW9f24k/HGb9sqXg5jjQ1cOO451sql+Ix6sXIq98Z7pukio3QdQTIOdmCBo+xu0MUsINyzZQOtEHddOJ+khREWs3XspQLEZ2tJenXn4ZVI3aqgoCwTCKECxtaSYUmCaVahKiWoqqmhJ81bW4jo0jJVpRBDXTgSadmaYUSHzpbJZ9bx7EQVIUjebPLSrg9YVxJ98MUx0DDjpVpQFs20UiURUVRVVZVFtE2pyeiQJB64p8PmpBXR2//fWveGXbNjKZLK7roCoq7e3tvPjSi3z8Yx/jtjvumMV76+7upq62lr6+PtauXjVvt2SzGUZjMSprqvF6dKTUARfD8OC4EsnMS9wEKoYpMbOCTC6F60iQNkhJNpmj98AAnqIeKhqXAeDxeLjiqitZtXo1xSUlvPLqDo4dOUo8lQFFJRaLkUnFicdiBV6UihQSQ9MZcRxCndupyAwhhMRN5LDX3slYNjMjUS2lJJ1Kg+2QSqsIkcHvD6KqKo5pU1xWze79nZzq7GbZsiYmeYKO14ul5pdqUghUFDI+Hb8nQDI2gZnJzuC+yUwGQ/MQn0jQtuf1PKhrOuW+IBvWNaPkxmb41gVi/QPkdu9j1YaLse0Ug/3D9A0NoFkm8ViMBWd2iJQ89N3vY2WzqI7N8b7ufHTvD/DyI49z08c/hF6cBy5V1bjs1ttxbIvuzpNYjo0QGi++9Do9Xd309veTTiVpWrwE255eAei6S2WTQWgiigxEkYaGZXqJlAo8ET9VDY0zxlM+v5dPeaiKiuEIpOmie324tkN+lWDjmpPzROICSTNFhxFBiMKZQwlxV+LkXJAS7fTcKeBIhydGUuzsj7EgUMK1C2oo8nuIRMK4ijqDYyiBXM4kWhSluXkx2Vw6f+jflShIJoZHcHM5xsdilJWVnHUz9hw5rvxaOWW5jKdNApaNrkhCgSBLKivYcfwU8XiCmy5ePVWREALHkcTTE3RlUpR4dDRTxZBeclmH504d5Q+WrpyqXwioKivng3fcRjyRQFUEOdNCKAqqouC4Lh7DIBQMnvblGWAhyDoK5vAEIn4CDB3Ttchkkgw6ndS65pz2CAknOzpJZbI0NNSTzSRJpDKsv2g9otAZk8nByb2ezo4OBkd6KSkKk8slGRqOcd0VV6EQBFcDRZkBeIsWNfGnn/08H/7ox7Esi6Nth+js7KSqooLWVauJFBXNuQRc1LSInp5eYvE4lZWV8/aKaZr4fT50jwfHcfLvf+miqWoeLFx7Bu1WSIGREDQtW4p0vaSTKXw+HddWwKgia9mMdByhsmEZKJPsekG0qIi7776Tm2++gYmJCQ4fzl8OuGjRIlpamgmHw1NRoZSQTsXIxUdQR04gvb68XgJSOx7Fc8efzew/y6Lj5FGGut/E5y8GxyI23kck4FLf1MLho/2ku3ooKopMlWm46jLaTrSDJTFtk7SZJWfZqJqGoiqIUABvUz26f/oojyUFrpshkU3hD0ZI57KgqIzFk6D4EcxeumfiGcq2boGqSlTXoTSbI2hmccbGKa6pmtEXeV9Jso5DUkrKAkHCqopQVDRFRdMN1DOih8mbKxqXLMe2bRzHZtPICEcDQa7avJma+nqaliwlGJy+eTebTWNaFlI3ULwKQtcQwsI2FQb7+2nST9uQEYLgompyiTS2beE6Tv4224iCKnzk3Ly9LgbCP73DJABLM4irIFQFFYniuowUVWDlYuhyJrVFSIlp5ehLZZGuRncuw8BEkpJknBOxJNdWXjdz4E5uxHh9RBcuZGhohJxpomsaqqqSy+XIZDRs2z4nxePcOS7pMjA2RmtDLSsXVBDyeRBScMmiRmxF0tbexfB4A1WnDTDd48MTKCGdzjERSyBdG8WBkD9AzhvGW9k4c+KK/LGJkuLiObSYW1RFxUFnLFzBaDKFNNM4jglWjhVFIbK2ZNZiUQgMn49LNmzANHME/QEmEgl6enrwCgvp2HmQL7wlVClJxsf48Xe/wsFDbxLw+clm0uRyJkOn/pC7P/L5eR1sGAZlZWVIKamqqprzmTPFsixOnTpFfUM94fD8FzRKIUhnTYZGO3EdF9MyEdJBUxWaGhfi9UxP3MldnIlElkCxQ7QkgKpKXMfBlQ6BcJhASTGpdDafYGcmy10IQTAYIBgMUFMzK9aYelYAlmljRRs52PYqWSwc10V1XMoiE4Q8QU7/liZN01i7eiUnTvYgVB0hVBAyn2e0U6xYHGHzZddTXFzE5Kt3waJGPvLlv5m6fth1nEJ0lU9p5HJZNFWfakcAvnAp7QfGOXbgOD1d/dhWDldTyQybtLe3c/HW22bZFIqGeO5XP2d32zGyuTSG4cF2bP7g+i2sWbditv1ScPXNN2K5Lq7jgABNUQl6fVRUlBdu/ZgtQgg0XUfTddZfeTXrr7x6hk9PzwPrHj+JjCQrXTyOwFHydJtsNoPmEUhpT/vW0LnxC5/GKYBifldyOmU/Wa8EIsVFU6kKBUmiu4c9//YbbMfCsW2C/gCDHo0P/I+PEjiDdiGAkaxNe28MJZnFceG3QwP80YbVXLPQQCr6LBqFqihkMllGEzFUVcfMZZGuB1V1WLp0CaZpFjQ7c8dvppwjx5X/vbC0iLJgkOqSEgQKriIJh3xcv6KFi+tqKI5E8nO9UE5RNAL+Ei7uHGL8YD+xiQmEoRJaUEHt1hDeyPkD1HyiKpJw0M/2Fx7h9eMDDPd24/P52dTayBUfvA6fLzzLbNu28aTG+N2Tz3NkIEZ8LH917qKaSm5paUDULikcOyg4x7FoyLYxcvIYuXSWRCyOdF2EEDTpccrsfoSoPevL4e0cVenu7mXVqlU8/fTTJJNJwuG5T3xqjsmp3S/x/adfI5VKMjEyTFFZJX9440aaAiZaTf1pgx+EI4nWLyZYNgHSwjQdpG4gpYJtjWMUl2KjFoDrndkgcPF6A/zs1cP84lmHWDqBLSRFfj/f/uxGVhozbRFCcMvNW7n1lmvPUuv04M3fCsFUvisvMw/vnn4Dw2RpzRsgovrxKQrCG8DKZogWl+MP+CjzK6jGTOqIIsEbkFy/bgFPv/wCluOCUDE8XrLZLhw5AtTOaAMBt99/L7fff+85/XSmTOf8Zvt4Si8p84eyi6t57fEnyNgOdjaD7g9wQ/MCWpetQlH0GeVCkXdwM7F0qCiJ0uhT2Hugg61XXcEL218n5xHYE6O4lU7+ypmCuEClpnFlJsaDz77AypVrWLpkKWOvP8ejew0+2HAVEcNTsDKfVvH6DLxBD2bGwHVdbMdCUTQcxaWmNMrJYyfIWpMcu3cIXHlRKQ6GKQ5OViJRpoBXUBaJTCk1Zb/QyHjLcbxhSqpdouEw8WyG0qISrtl0Ja7qmbWt+rZFCpRIDfVNyzg8lGFBWTGm5ZAywrxwZIgPXzp70quqRtGC5dxx0/V84ye/AUdiC4eM5uOxtgH++uYFBQvJDxavTm1DPQ21FfTHxlna1Mrw6DBj42MMjcYoCue30c9carxtUwpvwHvv+QBCCK684nJ0XZ+n6yTRSIQVq9ZQtr8DZWyUoGEgvT6Odw+zYp2H6jP2f4RwyQ73s/vEMNJxCIYs/H4vKBo+v+TiRTVkLAcplZn9OPn7tP4WTE/Wmbop6MW1lNctJuvZjV+N0NjQRGJilO29Wa6qnsk6Pz9/neUZOXPziDPe7VPb/bqfESWCRGBo0Ni8jN7RGH6PQWljK45UZk4CAb6iClouv5nL9vXw6kuvcPONN/HS9u34yhoxIrUgTgPUaYPOw553Lpqq41+wDDzPMhAfw+dAfHScw/EK2oZ1Nuhz31hyetR2rtMIEgW9qJabbryRI8c62LLlSgZHxxkZHUaEGk77Qo68KAL0YJiP/sEneOTVN6irr+X+++/jf37xbxgY6eIu2zmt7vwvA5ega/HaoUN4PJ78CzoSQfpUUskxfOksai4GVJ/VH+Js7G4pXTmnoZObNnPsKE6Gna6UxBOJ/C6eomA7DqqiEAkF83kR8e4O+E4RMTNZTNNE1zVcN9+upqoEJvMcU4nB6QlomibJdBrp5ONnVdXQNJVQIABi5g0KEklsYgLbcXAdl5xp4vN50TV96ibPSbvfjS2u6+aPWry6Db/Px6ZNl6Gf+Y1ABY2QYNk2qXS6wLhX8jkMBIGAf0a5fJJWkk0lCuAkURTyiVwhEELDb2hIIdAM74z5N+mHVDrFS6/soL6+lualy6aemYvSkUqnCpfQgcfwkDNNVFWlKDr7yx/ejpzpB9d1eWv/Xjq6Orni8qsoKiqeRR6edJdlWSSSSUBiGAa5nImqKvn+kwJlDuJm/urhQR59/DHqGxpYtWoVkUgYj+EpgPbcdpimSXt7B4sXN6HN82Utb3usFEDadR3iyRS2bfHySy8RLSpm7dq1eD2eWaTjqaLSJZPJceTwEVpaWzCMucnQky9ryO9ax+JxQsEg2VwOx3EIh0Kz+HuTZVzHZXxiAsMw8Pn9pJJxHNclGi0qXD8tClXnx3k2kyGbMxEiT47O40H+ogVDU/F4PCiqhjiLo84jOT//n+erVYg8DzscDPLUYw9zYP9Okok4m668jhtuvvM9eTtN2uT3+/D7Z395wVxKW3Yuj/qGQbHHw/SdjOKMJ6cNFAii0SJefvFFHnnoISqrKrjrQx+jobH+XYHVXNLT1c6D//VvpHMOK1e2Ulw8my80Gerouk70NOCcT0QBoHyhCOfjpZmSz4f8+Cc/59nfPcnKZU186nN/SUVZCaJwEy3kQSubiLG/7SgVkdD/Ye69w+SqrnTv30mVQ3d1TuqgnCUUEUIIIZAFDhgbnPM4jMOMxzPXM+PwjcfPhDtmnMb2OGIb2wM2WQJsCxMNSAghFBGSWmp1q9W5q6uqK564vz9OVeeWhMO9dz2PaNE65+y899prvWu9VFRWEKl0IQaRi12B/gApHUCD/X088ctvUR6Uaa+qZt2Vmyd/V5R0Q4GeLTDY0UtP5wU0xYOsSASjYVoXtRGtKhvzdk5837Zt7r37Fzjxdn7+yC+p/9dvUF1VNc3+N6FiZLJZ7vz+93jl8EGWrl3PX37qr/hD84ZNkuI8lGWFskiEJ3/3KI/e+xOioRDVsX9g+cpVs5ZhmhYP/eounvrtr6lvbuFDn/r0jCmDXGXM/YaqqmP25onZfWd6RwCmZXO+u5vnXtjPqdOnedPrX8+2rVuQpXEtuKTtKYpCMBQi+EcSg70GQtjXLpZpcOcPvkE2ncSjang1H1u37SQwS6bK1yICgXAEyaFBIrGKSZ6bmVKlDCVG+J/dd9MUDTOnqZmmpnnUVjcUn5/y8SnXJUcIfrfrXnbMq0QSefY/9wytbS1/dBsmigzMbYjxo89/gDP9cfze2QPF/4+JEIyMJOk8eZS/vvUaRvu7GY33UF1VgTTlWpk618HBr32FcCiAHS7jive+n5WrV/9ZrlACd0FKaYOFzVU8sus+1m3aPAWl7m5aycEEe/c8T2IgDgj8gQC242AbF+g63ckVW9YwZ0HL+IdLZ5Yis2X9MrRBWLO4kepYBHcjZ0IZk2U0maDryAu8ff0yzlu5S1hp/nBZ2FjBf378FqxcCkIXL8GxbdT8KO/YvJqD7WcZ6uulqbHpou+8FjEMg+//6Mf86Kc/JVIWJZ3J8vy+A/zj3/0NN79hJx7POJHOTFEEf6hcxn3NVQUd28G2bEzDdP/oJoZuYhQMN+XvFK25dNJrqkY4HEH1hdALORwxOfh4YvwSwg1PuKyLhAPZVJL7P/YxHv/xjzj45JMMDw6NfXOqxPsH+NWdv+IbP7uL23/yU+6+5376+wdmfX7s98Xg57e/8UZkSUL1+rnxhuuKPTOt2a9ZSmUXDJNTAzJPHx5EzuV5/ugwOcO92l1mj/xZJJFMcKG3l0JqhBdP9WLYrpNj3ALm/rRSSTbEAqiqyq8PvsITux9Gz+cuGiR+sT+zigC9kOfVU0dpXFxHbUsTkaifXDYz7T3hCDpOnCUxOFL0NMquF9IBzaNh5U3aXz6Fnh0HV47VwRE0L11DuKKapjmNmJaJZdnT6+ZOEQSC6tpa3v32txHyR1l35SYQTumjr7nfZ2+/INaylPbuYRJ2gECsfqzeM4nX52PNyqXoBZPrN21g0aJFCDFbAoDiGpxhzpXA21PfS6fTPLf3eTxeLxvXb6C2phrLMfje939EajRNaesuvWeaJsmUe5UsmUguOeYzyMW9ikKQz+f57a9+i55xE4UlBhMuLsS0MAyLQi7P+//xg8xdPBmmL4rnjcejEaqowB+OsWz1OoITdcTSndoyOXPmFKOpERYvXUkoFJ1xqU66X0sw+OJ+Buet5MDewzTveYbQ0kV89POfw+vVxgzmpQ7JZLMMX+hnfmAZiY5RfvDM3XiVEH/5sQ+MPTfTSWDZDr/fd4Lh/gKqYeCV/Tz9ciex6hwbVs1FU5WSnj1j548Zs2dpixCCEx0DfOOOZxgeNMAfJBY0yTqHueu5Tv7Xe5axuLb2j9q8XJc9078xMUygWFlpwkRLpNJc6Omjdf4S0rGlRNvKOHT8FCOjeTZvXD/mYLGMAqnDL+NYNo1ehZX1VVRYGV569EGufPPbkFVt0mk71k9jrugp/S6mt3bi2HR1tpPOZylfuJFuoeP40nR0nGDJ0rUoE/kFTZuu9k4kXPuJqqog3GugLMvYtk18KM5Q3yCN8+aM3S51XedcZyfJVJK8XYOwHER7JwOJNPV1ddRUj2PsSjU9/mo7Z8918/TuZ4hqFmp3noX9JvW1lay/YtmYbem1LtCJ/WVbNrlkGoHFvC1vRXXyZHWH5HCCoN9PedA36Z18Ps/ze1/g2NETdBxvJ3jiJAO/2cf2HTexbNkSVi5fOqmsZP8AXQcO4PUHsE3TBYcWNzFV1ciMppl71SYqGsdTapeVlbFixQpOnDrNvffeS1V1NeXlZWzdsmVaKnDTtDh7rpMnn/k9N1x3HX6vB9M0qKqqIhgMTtBmL90vl7wqnj51mvt+eg9Kzh18W7aRhSBjZLFMgSIpdLV30ra4bRpmAwSq5iEYKUPIPix7sqpY8jTc+ZP/pu9CP1quwFO7drNu23aCYRcztWLlBsomMMOMdYJh0J9Okxrqo7q3i55wgMCLe3npwH58wSDLly6dRHyxZ88eDF3nTPsJJCVMXhd894c/JZlMcO3Wq1m1annRVjbBzmEaPHDvr/n2Xc9h6jlqyxSEZDL42EO01IYJ3LacK3a+GSEmp8QVCLKZLPHhOI5j4/MHqKgsRyuGP0wURxIcONbDuX6D6ua5aHqKAeZQFbIpJPM88ugJFn6wqoiKdhXkrq4ucrkclM7bIQAAIABJREFUzc1zOHToEIcPH2H9+vUsXbqEzs5OliwZn5CTFkrRaTLSfpLESy+DZUFTI0ZZDMWrsWDx0rFAcMdxOHb8OMOJJCvXrMPjDzB/aTmOsOnp7SWbyxMJBV3jdyZPf+8FgpKKV0jsWDkXtbUJ/6IW2tuP0NS2GJ8nMIbsFkA+m+G73/omRtF4X1Nb657owNDQEAiBoqrs2Hkjy1asmtRnoeRp5lcGGXGCqJpCU12GZNcJWLJ28uxzHLBdZ41pmehGAa/X67rhrRJ2SWawd4DGeaXrkyBfKJAt6AwMDtHd3UNlyEMw4CdSFi3iyyfLSCLJT/9nNyNZE9tTixkKojgKwy+dBGGzbPE8d+MSUDAM/uOHv0A4DuGgn1AwgEdT8agK4VCQgM9HMBhEU2Qaqquoq3btnJZwSI6MImSVoJHFEynHKWjEMzp+nxdH0SE42bP49DPP8sUvfRmjYCIkF/gaCAY59b0fEAwG+fEPvj1m7xJC8M1/+AeM9nZisupirGwbxXGJg00EGUdwRf87ue2v/2qsDEVRuGrjRn77u8cJhEKoisp1W6/ls5/+NF6vNulMKugFNM3Dpg0b8Goe+nv6yBs6nec6afF7qGhoItQ057Ku15cAoLoMMDU1NfS0dyPL4JHdzSCkRBGqg24aHHvhGNe+cdvkd4WEqmgsWnYFZeXl9PaPsH7TlukFANdsfR2//OXPOTvSQz6T4oXjB7GsLLplsvWqq7jtXZ+iba7rTi9teqZpcmpgGOlcOy2xIJnKWk6NdrL7y/9CPJPhf93+7yy86uqx02f9+nWEwyHuufdecjkdTZPQ9QJ3P3g/v3vhCf7ine/i1pvfila8k0uShJHLEB06BGYWwxIkCn530ps6csHEHO6e0BjByEiCZ554lKZaP4nkKL0DI5iWoCIaYs6cSiRJZf/LZ3jXez5CJOqGVci2Q0uZRV3URDiQ9VegFNKc6+rFm03SEV5UTLXsToKhoSF27XqIq6/egtfrZfPmzVx1lcuWXGLh2bXrITZvvpqKigoc22K4p4dkKkksGqZjz8P0n+9D9fuoWb2G9mNHGDl8iJpNV9M2f4EbvlNU3QcHB+nu7aexsZFsJo0kSUUuRp2+vn7C89wMH1o4hLpjB4FCgfZnnqYxr5M9coJsIsP5s10MtM2hctNVNLYtIhiKoCgqtm3RcbodxxHYjkUyEXev3kLQ19Pn2kR9PjoXnWPZytVuDxc34ZBHcLS9h+alG6ivr6Y5aEP8BDIO0gSgjWW4ge6WbbnxnQhMy0SVlSIwExdxP8lTJiFZOUj3k0xkGTiXobxepbUhwHDfq8jBPE55DHkCUv3k6XZePfEKii9IY8s8PF4VkDDyWexMgtFUirIirsqybJ498KKLyNfcTIZj6cAlsIu4sZ5snvfd8gbe++Y3AiALB78HhrIW/kAMnwa2Y1ERtMgZo2hKGe6RMG79uftX92IaJoqm4vF6cWwHTdPIpDMUCjqGYbrQjqKqKfs8ZD0aAz39eD1e/B4NBUFAUcgbJgnLpG315ENEkiTWr1vHzte9jocefpiR+AgHDx7i/l27edNNOwmHQmPrXFUUTr56kpbWFob6B0kdeJ7YpmtwHC8dx44yNDJKhWVTX1dbJCaefQu7RCJBiITCCBkyRo68ZZLS0+jFLAqKpLjaRTo9y/uCZCrNslUb8Hg0Kione8lKGtrc+Qv53Bf+mU/81WcJx2pcBl00qmNVHDx4iG//7y/xu0d3MzqaGpu8wWCQt73/fVz7+b+nq7qKoN9Hg0jQYOQpV2V+ed99ZLPjWSiuu247H/zgh/jFz3/OP33xc1yzeT1XLJ/PzW+6gQd+8hPWr1rJQw/cQ3x4cFL7hRA4ZoJ8Zph4vJ9kKo5hZFAUgWmWFoR768mm85w6Faf9vIwWamPD5p2oWgXtXVni6QivnM7SdW4U74Q0H46RY4W/k3ftXEwqmyM1qjOYkfH4wrzthoV85fU2sqxOGHyJuuoK5s1tI58vkMvlyWQyjI6m0A2d1ra5DA320915zh0jVaO6aQ4BVeWhL32B/pOnMNNDjPac5fgLe4lVlrN41TI2b7+uqKG6GoXtOKSzObxebzHjhIllGiSTCUZTScLhCXGjmsbV17yOuVtuYO2HP4nvjW+i/Io19GUyZKNRzPmtpIwUzz23h0ymmIlCjPevKqv4PT5CPj9+zYtP86LICqqiuKDSSbcrgRKMUpB8DPT303G+i75RHV03x2O1ir2rejxYloVjj+fYty27iENzISSGaRIKhxjHZQn8ikGDL8PaFQu5aVMz1bUxEk4lLW1LCI52IGxzUpVUy2Bdc4TMYCdD/QMUChZ6QWdkoJfFvgTW8AV3HhbhQ7IMEU2jMhCkKhgi5vMT8/mI+fxUBUJUBvxu9osJcAo7n0Y/v5cyOY2qJyEbB1lC9vuxh08SGD0+xZQmqItFkCQXEiHh2vXSo2kUCbYtboBCllIiSUmSeeN73gumg6popPUCsu3glWQQEmF/gNpACF9xvrvmMIFp2zz6mz38+je/JZvNYNkmnefPc/s3vs5X/+vbJFKpsXr5/X6u334tigSRWITWnW8EyyJaVkHV2o1INbWcPn2GZ5/dx6Xk4hqXJGFZNppQCXmCKJKCIqtjuAzLEngkD6OD6Wn3UlHEZSxduhKPx0sgFCASjk57bixkRFJZsXItt3/1u+x66B6Gh/o5fOhFkkMjxIcTnO08Q+vCeUQi0bH3AoEAO67fQWtTCy+99BKsXUkhnWOF1891N+wYQ1JLkkuTFQqFCAaDNDY2snTpEmKxGAP9fRx8+RCO5VBTW88YyFIIRuND5PN53veuW/jmHbvdCSAEsqLytrfuINnxAonebiqbWgFoaKrlk5/+CzrOdHHq9AmOHzuO3xslm0uTyeS4+S1v4K1vewse7/jdX9Y0hkezvNpp4jeyVPpMPDEPphJBJ4tdDIIVuNirsvIKbnzDzRw88CJHjhylpraW4eE4+XwWxxE01Ddwy823UF5R4baluFKalizjDZ/7/0h0taN5fGRHRmjdvI1geQzHtFDHclK5VhvLcXAElJeXj21cwrGoqXbtEVNJVyXJRa43tc6lfk4z+pJl1OZz6IU8uXwOw9AJeCuKOdKKGDIhcCwLG0FXZxelGMmJRB4uQ477ewFIwka3ZZRgBH95BYrixRvzYmXPT/J0SoCsKZTXVtF16hxI7vXX6/GgG6YbL6iqKJpCXVMJ7CgBDlh5cukMakOA6LLVlElumqJMLoF/NIXXmZynsy6kcPWiOkbkMH0jJpl0FlWGqupaNl7ZhJkYKHUtAJosE1bcGElRzPThOO48lYTLMyqEg3cCg1Z+NA6OICf5CPo8yJaOY1pIwkS2THRTKoa4uZ5Py7TYPi+GmV7Kc8fPYtkmmuS2T5Y1lrfVM3LyIHOXLHNvF4U8D371GyT6+/EoCpJwUDQVCzdlkyVMenI59u95gmXXXuse6MAvfvkrvvFf30aRFT7+kQ8Tj8e5/8FdIGQe3L2byooY737H24kWQ9g8Hg+LFi3Etm103SAcjZBKjdLf34/j2JRFIxeN0y3JJW1c0ViEd/3tezl/vhu56JXRCwaF4pUhn82xeu3qaXZedzJLlFVUkMmkUWXlkqwy7mYU5B3v/ACWZdHX14deKKB6NKoq3QUz9XmAhYsXsWDRwkm/m+nbpZ+y7GHe/IUI4ZKDxiprXHq0aNTNlwWA4NyhfQQrqumxBBLCBXlKCo4NtU1N9HUeINnbRWVTG1LRhhAOh1i5eikrVy+dglqeuV7CcQjXz+foyw6DoXJk08KTzuOhQDcJ7CWh0pIFKG7AYbZs3caWrdumfW+mckq/q52/mNr5i6f/mzqd9y6fL4wFvuq6Xkwv7HqALMsqxpRNL6fUv5oWIxgsHyOElaRxxhshwOP1cd3OGzFNE1/JrqO5xtq+vj5GRhLoesGlb58CTxnu6SXsr2ZkeIiG2jpGhuKEkimmukFkVaauuZ6zr54B4davoBvIsjxW/6Wrl+GPFpMuFl+XHTCkEAXDwetzYx9tx6Bg2IwaCrExl0upvQoDg0MYVsAFW1smlrApC0c53zdE0O8bc5D4fV7+4+8+g7AshGNjW3bR5mZhWiaO7aCqKolsjtUrloz3rebHsAQeScOxbUyPB1tSMfN5FJ+XXF4nMGENjvRdwNRz9PT3Y5mmOw6WhWVa1NfW0DBvEflUfIzxKRsfQTd0BvM5PJKCIxwKahqvgGqfD6+kEaupYfk1rlnCQXC2q4t77r+fTC7DwnnzedtbbkFC4lxXN/v27cO2be78+S/YuHEDa1aumDQ3S2TKgYCfaCRMRawMWZYJBAKXTGkDl4PjkiRWrl/JqvWT06yMe4lAzJC3XMLFPyUTcXL5HOc7O4rvXabXQFVpahrHm1wK+/FasCGlusuyTDAYHNsQJ6O6JXLJEcLV9ey5ez+WnnezLsgGtm3iyBreYBjTsmYp5fLqJCHhsXRG2x9DKSiMpBMQqMUXbcRfpxCom3/RzWhie/4UfeSOpYTXp0B6hGHDwRECr9eLbpgY2VFWN1dTG52dPHZieVNp0EoYK6/Xyy23vW3Wtkz6zsSfkkrKV08imWbe3Hl0nTuHaWbw1i5ElA6nCW/WNdez87abxmxZJfor4bh2vFA4OCkIWiCBFkAKxfjO93/oPodAkaHK7+Fv3rkDWfONlyLcrJ5yZTODZ7qRJQkh8jiOTS6nMWfFdqx035jXWlFkli920/TMhH8eG8sJqqNAIGseRgcH+doP/pZ0Jk2ksoJFi5cSCUdpkYfYtPMdLvUfEgJBLj1K/3CSeMYgm0mDwKUAk9zrcjRWSbxjaGxsZFnhXX//Wd4DFGybYb1ASNWQJYmwR0OTFcIVFdQtXFCslKCzoxNVlmlqauSWW26mMhZDkuBTH/sI7adPEy0v528++XFWLJvsvZw6RzRNo6xsPK335XheL7lxzTbVp14TZhLHcVBVjS3XbKelZe6ktCazlvdnACxerIyp5UmSVCR2AMORGOgepL9/ANtxqe1dZhjBvv1HmKdq2MJNb/2HQg1tScFRAviDEsnsAGFrCDOXwmP3cMPCa/D4S1e+2b//p+0zl2XFp8Ly6iBff3API3mbnddfx1P796LqOaJcwdYp6Y5fS93+0LaUDpaqplY6hg/QP9BLY3MDx185TOPcjUwdAwmorK2ksrZi0r8JJnF9T3pBOCAkD6Pd5zh+/DiWgPTIMOU19WxfNR/hjRU3AGnsW4oAX1kludGjOLaNrCp4/QEGB+P0jWRY4C3CbYpX5EkVvJz2C1A1L3aonmMnTtA7NEJZeYQHH/41O65Yzgd3rkGbmDtfgOb30zM4zEA84abkFgJFUTENg5FEAtMWSP7xyItIbQ3Lamtm7fupIssS26/dyrZrr8G0LEzDKGI3BStXLOVLX/gcc5rnsGDu3EuujJnW4KXkz4qcVxSFt779PQBUVlVddqUuV0reLwmK6U2kIouShCiGG0wHaLgguxKrTylFijRlQkkCFq2/ijMDad777lp03cQxc3g0DaF4aWmpZdWCKyZBLsbLwMUiObbrpZLGr3lTRVU9RBeuY+d1aYYG+pjfXM/Jjm58fj9NG65Dic2Q35zxU6kEknUPBYE0SwyoECU6BMAyELKbM0ouXUSlkoVHRRYCVQ2y8sbbeF+4mo6eIa5av5ZYKEzBstm6eRMWfi7Op/TnEQmor2tAO3yAsD+IioImeamtaZ0yt0p/d1X8MQ1GTNZmpn1fktCi1TRv3MH7Ewq5fIFoKMBwcpTauho89XOnUXVFaurxtHew83XXYBkm+UwCfyhKJFZFXWM9eu8pjGwGX2hy4P8YPsu2sRGosow8AxGHBPgCEUbUMDfsvAkhHDZtvJKnn32WWDhMw5abkD2Ts0GEy2JsecPbkVva3VsREoo3iObxEwmHaF2wBKu1bazNU0UIF3gORc+r+2CpCwHJzUgrJBRNw6dpRduwm9Jn+7Vbx0DofxZV5BII5v+nxSU5LYjnn35MfOWfPi1+8M2vCb1QELbjCFvMTFRr27a49967xQ3XbxbveudbhK7rf1JCTSGEsB1HDPQPiPe/773itltuFp2dncKyrEu0wxaP/+YR8aFbXidu//IXRD6fExdjti2Rr37l9n8Xb3jjTnHo0MXJbR3HEY5tisOHXxbve/e7xGc+8UkxOOyScFqXIPX9Q+RPQeR6se8mEgnR19cnzp7tEF1d3Zdd1mupl67rYteuh8XPfvYLce5cp7Bt+4+p+jSxbVuMplLiLz/0HvGOW14n9j33jLDt2eeJcBxh25Z4+KH7xY07tom//fSnRD6fv+jIOY4jstmM+MynPyH+12f+UgwO9l9W+0dTSXHbbW8R73jHraLj7JmLEiA7jiN0XRf5fF7E43Fx9OixPxVR7ax705+Wg/3/gtgOvPC7X3Pr5gWMnN7vhhUBEvaMO70j3Fivv3vLJnYsq0Zk4xdFpQsE2VyWk2e7uP27d/LIE89hGMZF7+ESMNDTyc3blvOeG9fQfnTfmP1l9pcEw+fPsGNlE5mu4+Rzo8yMuR+X9tMnSZw6wCdu3sThZ36NZc2U9XW8UhYSTz14D6+fX8GWej/njh8GQC5hE/5oKYZsCYFhGAxdOEdquP81o8UvR55//nnuuusu7r77Lh544H6G4/GLho4IIejt7eXrt9/OL372c3Rdv2ibhXBIjcTZd98d2Cee485vfZWxXPZ/IpEkiZPHXmZRlcwHXncFh57bc6kXcISgKmjz1zevJ0CewaEhpIv2r2C4/wIraiW2tPqI9527rLq9cuRlti+t4gOvW4mZHRiDrswmjzzyCB/98Ef56F98hC9/8Yu8dPDlS5bhOA4PP/UMP/75L1xY0Wvo3su6Kk6s8P8JG9RrEcu0CYVDJPv7uPKKNoStI1xDhZt2Z0p1ZVli8fJVVDt9tOWzyIEIku0glHGv4yQR8OzBY/zw7kdYt2IxX/vhXciSxM5rN120XgsXLabek8ZrppDmrHF3zNmOCUlCRub1b76FvkN+1lzjJxAIua/M0t1CQHo0haynWb2wlu//43fYfOOttLS2oSjKmBer2AQXTuE4bLzqGrpfeJJAMEhra0sRYiBN66c/WAQMXThHZWMrlQ0tHPr9b7jimp1Fp8xkI7xt25iGjizLaMUr9+VkUxACDNMik85iWhaKnCeVSlFZUXHR9/b8+lE+/LGP8crx43Se7WDB4kUzRHu44jjgU2WiHg8tNbUsap6Lg/zH55Gb1A7B/n3PUxbxUVMe5umXX5m0+c6ULECRFZYuXkbaHqZl4UIa62svamEVQqKqLMr6xW34ZZVoMHpRk2yp7HWrl9IirUVSAkRbFs/88ATp6urm+X0v4vd50RSZgy8dYt3aNbOG0gng2KuvsqSthTueeJxCQSc4JRHkxeSSsYq6nqe34xiKquEPllFV3zqpMv+3NjIhBLZlMdh1kmhYoXZBGzWaxHNPPsTqTVdTWdGK6pmcwhjAMvNYQoayJspiFiODZ4lUtaEIFU2d2eAci0aoqa4mHAmjeP0kZwPcFm1np06d4qmnn2Z4oANvNs754XtYsX4Lm7ZsYdmSJdNsXa6Hy0Hy+KlqW47HpyIpchGk6eqPUxd9/0Afe198kX3nU/jv/TVH+5P84xe/yF984ANsv/76Ijh47A1sy03hm0mmSQz0UeZtpufVdvzBGN5oxCXonRDfOTo6imXZY/m9pkppg5luVxL0dZyksqEZSZJpbFtIfjSJP1LOGB6r2E/HD7/II7sepLKyhiXNc6lsaWbuoqV4vC612GybiiRBVLOpDglaFq4mnYhTW3Np7I/P40FWFJYuWzYjnKPUt+ByV2YMkLwBhtNpGqI12KaNI4lJ7nohBJ3nOvFoXoLBAKqiYhgGPp/XhRlk0yDLRMvKUVVlzPkjSYKO3i5S4UquX7GImqDEBruZC8NJGqpiKDOsK8dxXAiSDUlL4Pdr6LqBJNt4p5C12kJgChvz6b0M/fxu/DfVYPfFGd19HHXjZrxvfSPaBFxlqd22bTM6OkouncT015DKZiBbwC/SBIPBsZCwqVIdK6exqpwb3/wW9vz2t2zdOjMn6ISOo+NcJ0sWLURRNQ4fP86m9etmHfOpckmN63z3BX7z8APccus7yabjaHEvA91niFTWU9swd9bd1DWCMuM17I/Lx1T6r8B2TPqHT9K8eh5xTw0OFooySqr7EMH8IOHWTTh4xrQWyzIQwqG98yzxdIGRkSFC3iybY/UoksuWI0nTB2bNsoUkR/P84JePUFdTxfVXr5/1VMxk0nz2818kV1ZPYPkGkvFXke00T97/APsPH+HbX/vqWFofSZKKKrhDLpfngUeeZ8cNWwhXlrngTAS2ZaOq2jQYyfN7n+Nb3/k2NnBuII5QVA4fOsDjdWFuuH48HbKFq2kdePxJDv/yQc6/fJg6r+Ds4S66HjrAqWgZlVs2cPXffarIYCRhWRZ33PFTHt79MIqqomkasuQa/11NyeFDf/EBbr31LRMOMYFlmhSyabc9o0kQAiOf4XxfF81L1+ILRsbaoOdzHHvuObp7Bjh49CQdczqI+QKs3XYtm6/dhtfrc71/s8yVbF4nnzfwBcN0dnURCI6DjWeYMEgSDA0MYpsW2Wxm1jzwAsHA0Ajf+O7PGBhKkBmNcEKoiAeeJfrkMZobavnYe28eS1Rp6AZf+Pw/MdI7guRIaG4CPIQjkBUF0zKoaq3l45/8GOvWXVGsjMByHE5daCfY3Mz5SA0jig2Lvfzu2EtsXbSUuQ3Tc2ZZloOiaASr2ghdU4usqAjFiyQEti1Q1YlAEEHmxCskv/yveCUJ7/1xCtkCZn+KnhPHCeZGafz4J6ZpXkNDg6SzGZIjSRRfA0kzjtnZSbi8jLaWtlk2LgkL8IbCNDQ1gSTTNkPaJzHhL6ZpcuzECdasXMmCxQs503WOTevXXbZ//pIb1/x583m2eiEvHz3F9uuuxxFQUd9KfKiXWFU9Xm9gCv4JOs6d4/CxY+x65BEwTbweD7ZtYwlBfUMDr7/xRhrq62mZM2d2YOYl7CICUHBoC8dIaBrlUh3n4oeZu2wNleXVyH0vUsgm8YQqQbhekUIuDbIgnUtjOzKO5EXxaFhmDgywNQdf0A3onlwtiQPHXiWXzSJpXr5/z2O89YZNzG9tQp6yoUgSeL0ShdQQupHDG/BghaIovQbJ4QFePXGUNeuunNAQG8wCHlVm+aI2Hn3gMRauWkAmY7FsURPVURmhVk4ISHH7ZUVbDUvrwxzrzyIXQ0OWttbx6bdvReAgFSnKJCCbTPPAV75OWV+celkhJHzIeYEHA3MkTv+zz5P9xAcI+8sQsiCZTHLfvfeTTCYJBoNkc1nUIhOLLEkYhsnDux9h06YraWiod+1HZ1/l+L4nyaVTOJZF16lXUCQZwyhgWwYdrxzG4w9x5U1vwx+KcOFCNxlJpiFrsDhaRY8ER8530rN7FydOvcqaNetYumw5kWjZDN5YgcfnZ+7iFVSWh1m9cjnSLB7VbC7LM08+xZnTJ3n26Sfp7e9FCMHyFStZvXYdixcvRp64GAU88PAejp86QzhSRihWgYOMY1uMptMce3WUVCbngkplVwtKptIk40ksw42HVGQ33lPWXMC2CGgcO3acdevWFEdRwnYcurIBNGuYoKZhOyp+xyQrcMlsp60HkGQF3bTxezVkuQyEG3ZmmBaaAEUZ14AlJAKJDD3xQeTqaowhHVuRkUIhpNER5FyJlGL85iSEG8tpmDqmXmCor4+h/kGaF7Sh+D3opjHGDzl53QpwbK7csIGK8nI2bNiIoqgzr20Bg/Fh7rznV+z63R4OHj1Cf3yYaDBMXVUNWzdfhWcGDX+qXDLkRwjBtq3X8vef/wLpxAAL5reSSw0TCJVxMpMiGKygvnUBHq8PVdUAwVe++980xCpYv3o1Qb+HaKwS2xYcP3Gczu5uPv0Pn2X1mrV89yu3u/aYCQty4i4ghMCyTFLpFLKiEIuUg+SicAQgFZIkBocQ5bUkxBABBcT546i+Kwg0bUAEq4pngYRAwug9gTdazcolq9A8XhRFBccmNdQNg68iKhdAsIype35XTx/HTp2joaEO4di8dPQ0ViHLFz75flDkSc8HJYN/+ejNtFtlfOf+32PXlRPwRti4ZjX//u7XE6wqK8IPiq5ly0LvOYITrCcE7KiNcPx0Bw0rF1Eb07A7nkYsvnkSRgdgfnMDm9cs48Rv9o+N1by584iUl48vYMk1uhuORWpwiF47x3zHT6MviqSCbQlOGxmCeZXRwT7CrS5/gN/vZ9HiRex/YT+6rqOpbipo0zDdXPhCcOWmjdTX141hyLvPnODMiePugSLJSIqMLCl4PW5abD3fh6yoWEW8T1l5ObGqKhJtTUiSjHLyFAvmzCFv6Dy8aze/uPNOmlvn8u73fYA3vfGNk+eFIxC2RU1VDCObprlpzrQxK4nHo6HrBR66/wFURaKr8zz5QoEDL+ynctdu7vjZzwiHx6EEjmPz8qFjDPd20lq9EDkUYzSrI8sa2UyaTCrBmbOd1FXHinGHMpZljUFKSrgSBxtfSMPn0ShkU1w4f2G8/rh4sbaw4GzBIWMW0AJB4gWdWtlE0dNA1YQ2uVk9HMdGxsG2JWRJRhR/L0tjQJdxcYCIimfnGuSXurFMt99FPofcUkbZsgZwbJDHtwDTNNENg4KuU9fUSCgYoLl5DpLHy8nTJ6mpriU0S+pSwzRxhKCn5wLXXHMVygwarSTAEQ5f/frX+dXDu2lYuJKU4iHjWFimzr/84EcsWDCflobpbFJT5bKM883NzfzkRz/gxIlXeeLRX6LnRnEcQSaTA8ehpmke7/7gx6ivd2P9du7YwfmeXg4fPkw5CV7sTJGNx9m2ZR2rrriCKzZsYOuVV46nUBEOjx/aSy6dIeQ9ryCqAAAgAElEQVQPUHBs4pkUBVtnZUU5z75wCCMa5h/e+REkRUHCQSDjODZZDIZTfWA4xKqjpIWP1rqFoASKtvDi7i3AryjouQSeaB0e1c0CYToW/kgMOy7h9Xknqaolra+zu5+8bjCvpYxtG1fwk/sfx+/TZjRoF0Z6Ka+bz94HnkQ0taDYNpYJRgGMdJJQRb37/WJBwtax+0/hX72MlvIG1OULqVcUd6JmhxBGifV7Qs0cQXxomEdfOusGsAqBJKC5dT6aFkaULAVF9mCvDItWN7H/xHlGhtJk7XI0xyGfdzD9PiJXNqEP9UHbojEsjj2R6KBoSC/9lCSJ9tNn0HUDv88LxaOkoiyErCiTbDmlq6Tj2Cia6q5rSaKqupZbbn0nhTfkudBxliMPPki6p4++xDCc76MrnaB/KMHqtevcjWuiSBIFw8aSVAqGDZ6ZM+rats3JEyd55KFdqIClmwyc70SRJJeDUjjEh+JjG5coYt2aAhbhFQvo6OzEU6Xh9wfcq5hk06CkqZDz2AgkMa6thMsjWJbthoYV+0rRZHTLwpFVstnspDG8EE9yaMCmqmYenf2jeKU8AcmhOy0zkDNomRLnZNsOtl08hIUNJY1aAlXzIE3duGRBWvQhlrWgP/0KnkCRhsyroK1ZBfPLcISNQikMy7U7GobJSGKE8rJyyqoqcRw360hZrHzM/jSjecgR5Ap59u/by5zbbp1xPASCXC5PZ08vet7gzMF9tK1YhFQoEKutQRcCw5w9EmWiXBo5XzTABgMB1q5ZQ0tLC6Ojo/T3DSArMuVlZVRVVU3iv3vT9utxbJvMzTcjHIucYWGbJv6An7JI1FUjGbdXneg8y7/d/UMcuRjPJstYjo2kKmTaFmCUeYnHBzg/1E9zbYmJRyDZJmU+B4+vFp8IEi8MoPp86IaFz1eq+3i3FXQDQ1HxjhmuBUiuOl/I5lBtZ1KHOI7NS6+08807fs66lSv46LtvYTRXoCwapi9ucvJsL0vmj9siBGA5gpAKh1/Yz0gmj20aePw+POEwGUsmIimT7FXCkSjoJh5HQvMpY5NCQbiGWF0Qc32CEypm8/SzT9EzFHdzOTgOtuPwyumTqIGbJoTIyCAE+5/YRV1LJWLfcfw+PxeSCfyyjFdoRJsaaVy8mL5z7cxZtQHFF0QIh5qaGtavX+cuQGW8XiUykkgk7HpvJUgnRlD9EbxltRQKOSzHLmbNdQlSQEJSNLzBchRt3HuoqiqhUJhFK1bRPHceumFy7PBBag++zLvLy1m5Zh2tbW3T7R6SQ2VlBbHyCpKDF1CVmaMXZFlmwaKF+AIBLFlitOB6nBVVpTwUwZEU1CnXksxoinWV8Mz5PF6/H9tys2Igyfj9frbOaSR7/lXkDRsBFQFkMxl0PY8kKeOxmbKEnJfdeM9cltve/tZJ5fj8Emvqg1REytB9GggbDRuPDPgmswOUNPTxWNAJIVQTu2WiVopEJtFP4MKge4uQVTf7sONQGExjW34kZXLbbcdBVhTCwRCiaNpxvyuIRcuLToXZLVDbtl3HE0/8jmXLl8/6TCAYoCwcRVFlFMWDlcoR8fhJJ/NU1tfS2dPD/OY509ozVV4Dct7dBKoqK6mqrGRuW9tFnnRR8yUWnLLZniuecHlTpzoSQ/V4kIroYVeddjidyOD1KiSyec4N9o1tXLIQjCSSyClBJBAmaxWQhYlqagi9gOyNTLs96GaeeFrhwsmTqJqbkgfhoBey1AkPHt1wCSWK9FOyBCOpDIFQmLfceC0VZWFypoORz7Fj+0qyVm5auwPRKs4ePYDHH8aTNbBkD1bBJGmNkE1n8FW1TtLUHMchnrU5e+IszW0t+Dwqtu1gGjmkwW4iDUvdDWiCCBxsfZRoMcFiJBIhl88RC0hIE/j1XM3LJj04QDBWT1tZFQyNkFZsTFUjLElEgkGQfAz2dGOZJj4fhMNh/vXfvoxlmsU+HF80TpFX0uPRxhZQMFLO0nVXM3f5Ojd203FwHNfeA64GJysKPp8ff2j8WjZxYvqDIfxBuPra7Vx97fZZZsx4P8cqa0CRsdUAoUiUmbAmpTi4YChEY1MzIOMUN2Kfz4vX66WsfDLhSO+5TlIFnbThoJsylqQjkJAVlXAgTNP8ZtKZVGk3wePx8F/f/hqmaY5vLJIbnykr7sZlWTaLFo7HnEoI/A48dd99nG7vJJtNo2oaXo+X173heq6ePz30RkLCETCazSIrPmzHQJYkZMnB71UJlrL+ug1HQpA3BYlzCQxHR1gWkizh2Cah4UGiuRThKRuDLEn4/H5qvXXk9TyyI7szyHZcB9eElM9TN5WKqmqy2Sy2LaipqZlx03EhOoLa6koWtbSCLSivrkKWIB4fQTUKpFOpi459Sf6sIT+XEoEbqnPFvMXc/YWvjocVCFejksehSIDLK+OqajKSY2NoPi4EyjHjZ5EUCRyDSNRLwcwSlEp04eMdqKphdj16Nz99cA+G5areiiKj4XDH33+UNc0rXG2oWI5AZuv65TTXVbKgrRlJkphTXcZ3vvwZmAXsKNDI5SzWXruDYyc7GR1J0NBUx5ubBfXVVaiqZ9KgSggCksazJ17lzGCaVCKB7PMSSnexvVJHWrV1GhjNsWxaalqorozhC4T40ue+yL995d9YXlVGb+8QjZWLxr8vWSiSwnA8S7nj4Pi89GdS4HjxyjZ+U9A0dzXZngFKeGR3wato2szTY2q7FUVB8Qfw+QPFPpg8ucd1octAGEpj/3F3PKYvEtuBoaFhrlh7BXouRz5fwOudmcNIkiT+46v/eelyixKIhqhunU/YGqFSK2AhGM0aWIaOpMYI17WQOn8WWbgpu2VZYnUxud7sjiaY1HYhESHP9UvreGTXw9g4VFZWkcsbPP+bh7l18+pp+qOTjjN47iyPXRAo/goGE6Noqsq8OVW0Vak0GP2UzVmA6vEWbcYSFbXz+IbzBK/4PWRH4qiKTLS8nP+8aT2Ep4eSycIknO3m7sdf5JWznZiGyy3g8wVZ2RDilp3bETRMgywUCgUOv3yQrdu2kUgkOXPmLPPnz5uOQyv+/OcvfJ4SW1PJs16C4pTkUsiDP2jj+pMBUosDP3GIJKQxKniYPI+ViRdMWSFQXsPP7vsSkXCMpsZmDh7cx39/68cEgtNTPYPAVz8PW/Pj8florKzE7/Vxqv00sWCY0JxleGMNUzyEEj6Pl8VzWyZ9SZlS50niD9FbEIQCGlXRAKta60lm0ziahre6teRWGHtc9vooVLZRON9JIZlgfnMdR052U1lVT683S1XN9Hgyxetjycbt3NCZYO/RI9TX1vP+d7+fH/3sDvanH+FryzYhKW7yQce2CQYi9KlJTuVzVHi9RGONaJLKBVsnXBagdskC+o6+UCzj0uM545hP7Lcp35BmeqgoE/kui78YH4Np0IbiRiZg3vw2AsEQy1euLk58G8EMcX4TPN4Xm6ulZypiVQx4g2RT3aSSI2QyWUwbNM2DPuphJG8SC/gQtoEkey9r/ksTJzHgSCAFY6zcciPXHzzHvv0v8MEPfoA7fvwzBkdGEf6KaT1lCwnHhmjQCx6buJNBdbzEgjKmrhOsqkNW1LFyJASxpiU01M/lXOocNbEKuju7CISi7D87xJKbFk6uI+DR/Pj9Ybo7Ozh+4gQDvUOuOaimhjmxzfiDVTPirLxeL5/7wufxeDxs2nTlGO5PFKf65Gt+KYZ44ibF9LG+VJ9eAnYw6R9LjxYKeXw+n3sFmA3a/QeKEIKhoSEOHz7EvHnzaG1tm+56Fe41yBE2iZFhCgWD48cPEwiE2LDhSjyeCbndx079Ip1ZKkmhUEBT3atOQS8gAZWVVZOAllNTbDiOw8GDL+Pz+ViyZJGb4G7qiVJ8NpvLYFgSr5w4Qu+FXhYvXkpbyxwCwQASU8Ck7otksnlMy+TokUP0Dwyx44Yb8Pm8+HxFm9BEnUUIwCGbKXCmo52ujm4a59Qzp7nJZesuK59wkjno2Sym7VDI5lAkaSz3uy25/IzBsihWPofmD7p4rT/tkF5U3HFxOHb0KAMD/Vy56aqisXyGSgiBkxvBHD6LVLUEze8a5fPDXdB3FP+y11MK7H3N9SgFrTsOZiHHaE7Hsq2iXQc3+LlIWBrwqGiBINIMAdGX22a3LMHIyAiPPbaH8vIy5s2bTzgSdfkbZWnymOMa6A3TxLRlzp7RqaiA6iovsiyhqeN2SGlCe1KjKUZH0xw5dBCv18vy1WtRZKiMVUwqo3TLQYJUahRdLxQTF4AkywT9fsKhUDFh6hTWcMfEQOLUwSOkknFWX7kJv88PsoIkXxaV2Gwya+detsZV8jr8zx0/4tjLB1h39TZuue02N63wrO8ARS+Lm5dp3Gx8MfnFnT/iwN7f44+W8a3v/GgyM1BxC5cAGZlYrIrnfv8EP/3ef9La1MjatWsB77TJKyEhyRKx8tjlNnm8HcDLB17iq//6ZcIBP2//4Ee47obrp7WiNHECAT/5oRGefPguTh47QmbnLSxZ/FcztrukQwYDPs6d7eaH374dYets2biGsmjrzNqLJOEIGV/Qz8Hf7+GZx3/Lpm03snL1Z1CkcUO1+6yMLxTGB4SLNkfHttENc3xTlCSU4OTsAn+sXLZWLmBkJMFdP/kug70XOHXyKJ/8q88iTaC5n/y8QzaeoivRjtfjdfFShQRzA37AROD5o/ZdSZbxBEJUBv5IxtKLlVGsoSMcTD3P/KjOqVf3EfYozNu2Y7rWWPypyBI+j8YTux/noft2s27tWj7yyfe5ySpm0DQBwuEID9/3K3730N00NjWxeNEiaua0uFrPxC6WxuvlEg3PTDY8c9+q2GaeFx+7h2hQYr+lc+0Nrx+HOP0ZTsLXdFW0LQuz5zSvX1DBI3vu5/U3vxFVm3mAx0IILAPbgfRIP2WVdRfNyVV656YrV/P+jY2cHrUvnQ1Rgrn15fzjbdcRqageA69dzvXgtYidGOTGJXU01taQ6j4NXD97O5Aoi4b5m4+/B1G4kY7eHJZj4ZEVZvJ+lerZWB3jW5/9ELKZRPHYsz4L7qSTJMHbr7+ad141j0FDBcdCKDMTyY5rjoLDLx8hGo1S31CH/7JYwC9PJoaNHDl8hObmZveqUV5+0XHweBQ+dstmylSHfeeSrreSUpqeidqpQOQS/Pjnd3PHbw/g9WhYlsXKFUu443//PcLlmZixTv+vxdgCIEkkR+JUKgopj8Weh+5h09brkWYJqwFX8zfTA/z9+1YTCfowbAdvKe3MzEUwt76ctR+6GRQPkXCwOKvEOGzmjxTHAcmyWFwVw+ekSFsZhFS0mIqpd8U/jVyWFlcafFmRiYaCeDwhPvzB94+BEWeLyheOTc/pI3g8HiKxWpc4dpbvuxghi0RyhL74KJ3nE3gDrst9pu+P/Z8j8Hj8+CNRUgWDfN68aJaA1yalxW5T19qKaUv4AhFed9u7XK+nGGc2ntgOR0iYwLETncT7B8kbYBqiuCBnvl4WCgXOD45w5sIgPYNxDhw/g2GY0/psvG0Cy7QZdDx09vQzNJpFt2zX83ORmdJ7oYdzHZ3U1NVw4IWXZm/5hHF9LX0phOD3zzxLMBSiPFbOs79/jr6+/ot+JxCMkBR+Xj19lnCkjIIxA/EqABKy6uHg6Qs4ju1eg9KjJBMpRpIZnCmvjGOTjMsiHr38tk4e9xn+uYgJmwEYOqEcYRUY6o1zqqudZavmu9CM0nyf9HzpjyCftRgYaKD9yDF27VWIj6SwbTGtmLG2CMGiFWtQJYEUKMcfiBZTVE+dI6LYrunjPlNamYnl2Ogkuo4Rq/ZSv2guEZEjN9CNKewZyNzGv+dyGVgugPc1rlflS1/60sX+/UulQo4df4WXDh5m18OPUuVzOHruAke6+jEMk+qqymnkCSUx8lmCZZUYeo7h7nZC5TO7SguFPHffexf3PnQvHRd6GDQFnQNxHtr9EBUVMerrGyafvsIhm9M509GJaaloHgfLiZDJyAwMDeOP+PGWkvxNuPtPm0djqUpK357gAxOQyWQwzALnezvwSVkiNVWoZdU4KFiOgUebbIOyLIu9+1/gqWf30nFhkLPnh+kYzNLe3oWQTBrqprZDcOTIET78lx/hzv/5Bbuf2ceel87w6K8fY/fDD9E8p5nm5uZJ7ziOQ+f5LvoGB0mM5jA9IbL4yGRzmLZNWSQyq4bxvf/+HstXLGdOcxP33nsfG6/cOObGL9XHvYoK+vsHXF5In2/ad6a2odR9zz37PI/9dg+3vu1WZFkmk8nw3e98j+3br5tEBTZuD8zR1zeAN1hFZcgm0LiKeDYPAgJ+3/SysiOcPHqYM+f7qaypRtcL5DNZVrbW0Tp/PrI3OPa8ZVl885vf4POf/zyqqtLT04Msz64BSpLEyGCcvU89x6kTpzh7sp2zJ8+4f06dLf7/WRLDCarra6ahwydOLcfO4lg5JHl6G0qS6tjHr/5nL23LF5LICdo7kiyv0/FUz0ORFSbqmrYNe5+7wJe/+CKn2gssnB/gsedlHtndRTpdYOXqmgn4LjHW/tGRUVKZAh4VvLFmFF8UXTdQFBVZUcbxhEUbl22aDA/20N/bzWBfN/HBPjKppEtN6POP29GKTg/Lsujb+zjWy09T65XxGxbOmVcYGB4m3LoY1esbs6mWJJ/L8/STv+enP76bx3/3e/bvfYl0Oo1jO1RWVU689f7ztE4rymVdFc93X+DDH/skydQoCjaDA32M2hoJ/XnCoTDf+OrtrF+zCuQpAyTANAo8dfc3kSSorG+mpnUJsqxOuZJLjCRGOHLsGKZusGHDVQRCYTRN4VTHvTz8m920tc6joqJq0reHuvuxR3SsqJ9g40Z8ZRkQDkYmSyaRJRwIUcK0uK/McMoIMT54k56SKBgmZ8+dI1zupa15EcqCWmTTi+TNYBoZkEKYto02QbUvGPr/z9t7x9l1Vvfe32e309v0ptGMNBqVkWTJsmVbstywMdUECAbnAiGBQBIScj8xN3kv5Iabm0DeNwkkEEicnhASAibYBncJ96peRnVGmt7PzJzednneP/Y5Z6oKBO7SZz5Hc2bv/dT9PM9a67fWj4OHD+H1B6hraqdULBLSVRLzc/Se72Pb1uvwLcqaKqVk/3M/om9gkFBTm5sVIBgiXGMxOjzINx56iL17by2DQN17HGkzOzePZdpEo7Ug6gkLSSaTZGYmTl1NLYFVbDSFfB6v7mXnrh1IKTl77jyTE1O0trVQWbCllGRSKf7mb/8KVRHkciV6tm5h2/ZdfO97/8mWnh7uuftOfJ6ldsSRoRH+4k++yvFjx/nYr3y0+ie/38/Y6Dh/9sdf5n33v5f1G9ZX1X/HcZiengYUvIEQJXU3iqqh2Q6FfBHHkajqMpbxcBP3vfOdvHy8n/d86H6SiRTf+ua36B9PcJd3IcOolJL+/j727z+AbTt85zvfYW5uDkP38ju/+z+46667qgtyFVxr2zzxvR/y1S/+BaZloggVrYzJqhqvEWzZ0cOXv/kX1NbXYtsOj/fOcmFqhk0NWdqiWdaEfRRmX6KkNbGu7W6E3sJKcSgkZjjTf5az/bNk8il83jmmBxuJbSsfsRZ7klV49aU4Qg0iZYHvPb+WVKpApFbwysvD/PwHeojV+igr1O78zRUQCOrrm7EjUUxbIi2JJR2KWgFND1THHKDvzEnOH3uVVHKeCjBb19yQpmAoRLS2me033UFN3UI4kqqq1DTXkBqJkJyYxedIctJhzaY1+D0G6iqn/5mZOH/+Z1/FLFn4fD6klBx84zX23b6Pru51q2YjWS7XgJyHyclJRkfHiMZqKBZM+mdLaLpLoxSfnuHkqZPsvmEni08tjm3R+9p+3nj637Bsi4DXw/TwOXRvkI2772b5UVWm57h7Vw/jSQsHjZn5FOGwH6tkUcoXyGYzSxYuu1hCzqcoJjNgaKRSRVSPn5JZoFDMUZifgLbGqk82PjPF0088Do6LnA4GgwQDQcLRCJFIBEUolCyLxpZWYjE36b9ZKpFIFEgmpvB1NuP3me6LJRXyZgrL1HAciEXDVDwG0zMzICA+GycUimBZJh6vxvjEOJlshrGxcdZ3dJT7VpBIJnlm/35ymRxBq4Re04AvVsf0maPMzcSZqpleOLKXp+Xk9AzFUol0Mk00GkVTFUyryPT0DDU1NWTS2SULVzqV5st//FXmEvOcPd3L7FQcx7bQhM5f/8XXCfg8fPb3P4dRJsNVVI1UqsCzB/bTtmYNF4fGefmVN2lta+HkyRP0bNlI+5q2JYG0z3z/aZ57dD+GpvPQVx7iwrk+dEPnyGuHGOof5NKxC6Rnkvze//e/Fp3OBULRmJmeoba2Bq83gBBQzGRJFtMEUj5qFgNEBQjDT/tNd1Pb/J8oisqHHvgQP9p/gP2vH+KTy7JlnTx5iny+gKa5oF5d1zGtEl//+tc5cvgIu27YxXXX7SAWi+L3+90TiEfDFg6oAlVRkIqComg4UoLjYGg6Hes7qat3cVC2Ixg+e4IT8zanR6GrIUSLd5bRXDs9dUXwl4gFS0T8uhvDKQAckDaFXAlfwEshqyOkgWZoGL4AYoWnXoCU2JaCpvuI1YYQ0sEfgtHxVwn561aqybgbQzKRxOvzEYvFUM0SuWyOfKFIILDUSeWqpyreaDPJokq0pp7t1+1gbGyEY8eO0tq8HstySGey1NQ1VOcvgEdV8Pg9ZHwNlHQDdayELJZwhEBZYUmTvPTiSwS8UTIyjWlJFMU98R85dIyZ6Tgtrc1cTa66cEkJj/7gB+iaTnx6Ep/Ph2IomGaJQDBCsZBjamQAR0jUsqtUSkjPx3ni23+F36Ph83opFktk8wXGLp1h441vWeYFkej5eXZu34p1fpxSwUJVFLLZDO++5252rV9DfXRpvm7V0KkNxYl4HBK+VoTjIHJFnEKBqDVPJDMB7KgWMzszzYHHHyUS8qMo5Vg6KSmW3CBi27YpmiYf/OgnuOft73KPwYUsDdo08XmHVKaI5qnFFrMI6SedzKCSpGh7iUS3u6+MhP7+fgYHBrn99jtcYKSuIR1Jd3c3uVwORy7V+kvFIgODAzjSpjA/hzBNcqMDFHOZZSqNcJMRIjHyM4RlhkBTk+tyFoJSqYiuGwSdDLaZryLchRCYlsX+A89RyBcQ0uaxxx7H7/fT0NjE+PAI2XSS93/4F+guo7sDAT+/93ufIxIJ88gjj1ATDoFd5OL508wnU/xTLsHevXvZu/f2apzf/R/7IN/95/9EFCCfzbL/u09jSQshBR7Fg6Z7eM/971twBsiKTUWQyRVoaHDpqvL5PKZloWOjmBmkDFJJNVQJxg8FA0SiEQTQ2dlBIBggkUwgl1lUbrnlFtKpFIePHOHSpUsYhoe6ujC2bfPGG6/zzLPPYpomH7z/fn77QRdUrCs6oaY6hKpgGHqZ4cigVA4Oj0SiRFprqycij67w6Q/cxXsnp/mj/3ieM9LHiBLBqzocSgty1jDjmTnefd06elorCH+BdBxKRYtkWsGvGSgq5PKCkbE51pdDqSriAs4FuWyBbLZAMBADRcU2SzQ19pBKxhdsoRIcXN5HOzeLJgtYpoqDg+1IvAEfs+ODOMY8+Huq3gwhBNHaeiam4+TtWZJjU+TNIxQKBULRemqb2inkC9TU1pWHzwGhAA6OWWB4dISa5lYsyyGbLeCdnyPoWCvcS1LCvttu41/+9vuomgGYOKqD7djcvGcb9Q1LSaMvJ1dduBzg1jUB9n7+U/zaH/xl2eBpI4SCZRX57EfvY0OdgeOYqGolHYdgfPgiQkAqk6FkFvEZBo5QGOnvJTU3RaRu8aoqcWSJyakU5/v78OgeAgEfqUyCrC5w1q1BLCMocBQFrw7D8ykKYYlX96BbJmrRJpNJE/bkqnVx55iCohpYtkCrun4FVskin3N3ZUXVqMT3AZx7+SniA2eo3bmbbMEmIn0Yhg8hGglEAky89hj5vELbhh5UzY1/FIrCxu5uRoaGaGpqQhUK6XyOWCxGMBhELt8ay6Pq2uMkpVymfMJS8Hm9bsBuuY8qsH6/4hDyaiQVBUeCZbmhOZGwnzWeNGjOEltScm4Oxyq6KZqFglQ15uZnkKjYjoVtFXj+wHN0bVhfjUs0DIPPfOY3uffet/Ltf/8P+voucPToMUzb4tCh4wwMjLJ27Xq2bHG5/0LhEA98/AEe+vJfoSiusdij+jBth5Jp8d7738OuW3YuQdMrsoB5/nFa6rfh9XhxHAdVdek71OnjmHYTor5pJexEFUTCITRNw5ECw+Olp6enSnwC7ovY3t7OL/3yL/PBD32IyclJvvvd7zI3N8f8/Dy6rtPt8xMMBrnjzjuqY2GgEs2ChkPWzAASqSqYpSK2tCiq84ienUvK0TSN9tYWvvypn+PVw8c4dn4Es2UnuaLD+WkLNTeDmk6CjFbwLKDoCI8PbAXhEXi9XnI5H6f7p7jdcZY5lCUI2+VrtMxqcgI37KoGn09hwVohcXBQpWRu4AS64cPfsBnpmJimhcejUczMU0qY+Fq3VNsgpaShoYFYLMbWrdt46aWX3HmFIBIJ09rSRjgcWqrGSQnSZn5ywjWw2JKClUPTfRSLJWzLRPOsVBVDoSBd3WvpuzCIECBsN9nBps0blrB3X0muunBlk0k8SF4/dQaBg21TxmRJLLPAhk1b0Cb7KGTT6OFK6lxJ39FX0MpqmUfVSKYyqJqOR89gFgsryrENL0dPH2F0cgpVVZDYlMwi69Z24PP7MbwrMwDkiiWyOQUtbaGrFlK1KUkLSwpMI1o9ogoBkZo6brn9btcYL2U5i4GGrqoYhuGyHHs9dG/aDAgcy2J6oJ+29T3ktCgIiWXbFJ1GUArohp9U2mFufBDLstA1d9G2LZuGhgYe/+HjpMuZUnOmSXt7O3v27Kl6uhYMqW66mJJl4/H7oMySLKVkYnKKDRvC1YW0AvrKF02KjoG180gAACAASURBVEBTNaSUlMwSuqbh6Aaa4cG7yDgvpeTihQvEZyYpFYogHVA1imYeVXGTBEoc6utqV/AfGobBtm3b6PliD/Pz84yPj2Pb7qLY2NhIY2PDwvWKYPe+G/nOt2sZHR5FSgchSnh0H7t238D7PvzeVaAtLlO6je7alFxAElJKElkTzZenXqiruO0Vfv2Bd+H3Gkgc/uQz9+Np3bIq1MYl6Q0RDAb53Oc+h2maFAou2a1hGFW1VS6CeTuOgyltssUshqohy5uddBSE7aqLK7z8AkKhAPfesZd773C/sizLxcipS5mk3E+FhliM67YFODsRR0gbTXcI18RY4eyXAA71jUFOnXFPlqFQgEwm4774ig2LT5tSIoWCXSoQjjXiqF4KxSK25VCQNpo3iEW2fGJaGG8hBB6Ph/p6D+9///uq82fxNdXnU95HpWsbrFxXLJbI50tY6TzqKtAcIQQ1tTH++Ctf4OAbRxkeHsHn9bJ9Rw9bejZxrXLV1M1z8WkSmTRDiTymaaKqerWitq1y4uxF2kQWvzdQ3SUEsPvu99J13S2oiqiG9TjSPZGEYiuPg4FADWtiNSR8HczMFZibm2VDdwtBxSSVTFKvLquqEEjVYCAxzcE3HsaSEluxMa0i7++qpWf3exdfTGNjE7/y679WfvlF5evlLXa/dlwIRL5oYkTq0cNhvIEahBMnnzPQfO0UrSy1XdsZ7TtfTnvl3hv160z0jXD+bC/JVJ68mcPjDTA3NsTbbr6OpvraJQ4MIeBdt93Ai8fOYqs6WCZSUUFXaG+s5f6fe8/CC192NJiWQ/+Zc5ycPsUte26mkMsQratj6Ox5YmtDdGxcCh7c95a7+NpDf4lplhYZmd2xUBQFwzDKwN3VRVEUampqqKlZsIus8JIJ6NmxhS997UtMTUyWkw66L8bW67bS1NK4or8dYVBX38zfHzjAxGQcXTfIZTOULIv3bm1kTVsHKzgQy/OrbfMOisOnwTRpbmnDu75yeli9DZX6GoaBYaxOrCaBG2+/iaaWBgRQcmw0oWBLB1XVsMvq2JqO9ssioBb3S2XcVsTsSQfTtDh9cYbByQlMqWOVciiqYP/BE7wrlaK2diGW0MVx69z33nU4+QFozjMyeonOjXXowiI1eg7EDdXFVxUaoFDbtJYn9j/H0dEfUipkEFLF4/Vyc1cjjXu2Vyn8riSXx7+VM68IjcGzQ+TODVLsH8FRHIqWTXTtNjTNw7KjY/WZsViUe99+V7k/Kt9fpTKL5MoLF5JcOk2h6OCPNZUTBVZ0bjfJ36FTZ1l/6zZUfZHLXEBTRzdNHd3XWA2B4q9F8dYR0UMUZY5U3mZNWwc9cgZdc70liztAQWI5OlMXDvH6sQEmR8eQjkNt+xp6vJvZa0TKdRVLyln8sVo9Ku3WdB2hG3hrGklZCkFDxXaKmDJIJp/DdmxaOrs4WnLKPW/joLJj83oCySGcYgbLsvH6fdiWJKQ6bGoKEgn5loxQrLaGP3zwt/mtP/8mw8kMMwPnCTeuQdMN/vSjd7LrjgUktQMoQhKsaaSEwZtHTvDUj15DtXP4Glr51K1rsTNryyoX1fZ7PB7e/q53VAZ1SR9cK37magDOCkP0rht3rvrMVXM4SSjMznP2jVcYzUsmxobx+QO0t6+lUKoj2LwyoHcBdS7IjF0g2rSebCaNlo6jhmrhv0D8IYC2zjW0dS5iUF80JyonM+F+cYV5dPk2V55aLBY5PjHPUHyeLZs3k0qpFEslPvyRX6RYXCUfvpB0tFrcuf48RxC84+3tlGyDwYGzdIRnCOgLtlCBggWEG1qpUR2OHnqTRKEAlkVzSz1qaoRb9+7+sfpmWcOqWgEItrzzAS7VtvDEv/4dN2zbxljfSW6K1iPL6auu4XE/tlxZVZQQDQfZtH0HATtAXW3MVTdw6bwjkQhb17XTXAFfLzYkVx6w9HGuZWmVcBwlEMauX8vOuhiGIsmVJGapRF1tKw3NjatMYKjbdDPdtxW5238Uj2bQ1trG0PQkLes7UYOrhyws9tAtqe3iOgkXFNGxcTvx+CwHe/vco7EiCXi9qKpOLFbD3Xt3s2Pv7Si6joOKikSJtlC3427e/YFRpCPZsqWbY8dOYBVNfO07kcpSXIumakS6r+eOu0eZnpmh6b57GIunCAZ9rLnlrRixBXVMARAKwaYO1l2/l9vzHhQzQ219HePzGeq6m2m5+RYUsYo7WVY+ZEXzKDdZICkb8q9pml1eKoDH8s6G41gIVXdLkGV1ZNH1iqrSducH2TduMTubomtdJ8Njo0hFo/u2WwnUrV315RdCoPqD1N36AaThI9azD1TtCvUvqzZOOXfuYhVp+e+LVGwpZfVwXjkWXDYDhOPGwjqC8viuTihTmX/BYJDbb9/H9MwM97zlLiamphgbGeat97xtBdatUlNT9aHvuI/OdIFbdmyib2SKoL6WPfe/E8PnZSE+FTQJTriJXe96gHeaIRzbQRMaiibxBGqJrN/OVVfeq0ilnpH2tWyIvp2GuRka99zGxKGX6L79bjeafBWTVaVvlydUoGp7vgZZDRW76OcnFseR0rQs+fqrL8sPvO898vc//z9loVC4MmmpbcnXX39dfuyBn5d/9zd/JS3LKhN4XpGPslyec01kny75qiUf+sbX5Afe+275rW/+0zURfabTKfnHf/xF+ZlPf0oODw39VMlOHceRJcuUj3z/Yfm+97xNHj74erXtP70y3J+5ubj87c98Sj746V+W4+Mj0rEdaTnWT4UQ1iWdteULzz0vf/1Tn5IPfeNrsmSWpOOY0nLsnzLl7I9VMek4tpyenpK/9olflL/w/nfLc+fOlufL6mNv27Y8c/as/PgnPiH/7E/+WCaTyasS7ibm5+WXvvgF+Ruf/pQcGRlelRR18Rx1HEeeOnlcfuQXfl5+8Y++4JK7LprHi38mxsflM08/IU+fOiGnJifK/fqzI9z9WTx7oQxbFgoF+Z1/+xf5iV/5kPz2f3xrUZlLyr3s2vQzJIR1j9R+UeQT991MDXNIx77yLUIQMSQfvHM7IjGCdEzA3cWutBA7jkOhUODMmTPkcrmrqj8CSb2W5CNv2UiXP4k0VzoLFlrhLu4XzvQSLkxy5zo/zz375JXb8WOLACE49vITfOTe7Vw4/ko51e9PU1z4QTI+ji/Vz80dBlMjQ+WYMrFg9/svynxijpeff4pGv4maHmd2Zso1ri9/fDmEpZjPkJqfJpOeK0/Kyz/bti3y2UQ5SeG1S1U7NnM88JadvGV7K8VUvPLtqvcIAU899p9sq3MoDh1leKD/KmUIJseH2dJssHdzA0cPvXGZ5y7ODiI5/NqL3H1dO77SNKMjQ0uuqVxn2xbHj77BDbtupKW1DbNU4uSxo8zOxn/sUJnL1r+8ILz44os8+OCD/MM//AOpVOqan1+5f2BggBMnTlwxTAgEpmly5OUf8nN7unjzRz/AKhbLPXJt5f1MEwkKIeje2M06Mc3mdW3oy7xKyxVJKQXr1nVQk2plk9SpEGMs5NNezU4iGRwcZM2aNXR3d3Po0CG2b99OMHiF4G+hcMuefeRHT+Jt24IwfNWOXakKuC+8ZZq01PnY2VnHi6PWUjT3T0EUAR98z73UKmnshq34y+SYsmxHWF5Upb7FYhGPx8A0LXRdv2KdHOkQi0T4+Afuw5FFatd14Pa78hPZGVaT0aFBhk8f5sv/81d56oWDjA0PUVffgKrqK+xqxVKB4f7DnDr8EpZVYu/dH6SlfTOUCVQWt9VxbC6efI6zh5+gcc1GOnvupKFt048xBpJYLMbm1jo2NO3C09oKVGJHV3+GrgjefeeNHDt9HstebbGUVB06SIaGLxExFGo9MY4PDLiqqboynHnhJRa8421vg4ljzOZMmptbqn9frl4m4lN89U//gDVtzdz21veQSacwSy5TT3mGXGM/XF5s26bvwgXm5+dJJOb5y7/8Sz772c9WnRlX6+v+/j56e3uxLJvNmzevQMC7aB63zxwp8XtV9l2/hcNH+hmdGKd97Vo3ecA1HKeuGqvoFniVE8wKr4mL7xgbHSM+NUNibpxUvoSnrh2rlMOzKOap7OZCOhajo6O8+tLz2KlJdI/BhYks6WSS2rq6au7y5eU4jsPZs2fp6OhAURQuXLiAlJK6uroV4UcSl84plysxmzKZHh/BCnYQCNeAlFWSh8XPtyyLJ558ipcOHCA9P8PsXIKTg7N4gxHWtrvsMkuKuYYdankZ+XyeS0MjnDz1OhHd4rW+NHO5EsIuEY1EXWPoKpMmkUjw2GM/pKtrPfv3H6C9fc2q4RIVcGLv8eOcPzdKSJtnaHKW59+4RCAawzBUvN6yjUQ6i3Y9p/paVCZbZRNZbcwty+L1l1/hwsnX2HPDFh557hDf+s8nQChs27YNZVE7TLPE9x/9d5qbmmnr3Ext41pyuTSBcA2G4a0agBc6TSGbzTFw/FmmJ06Tjg+ydvM+FPXKi3VljgwPDROfinPx1GFy+TyzJR9C0/EFgkvqJcuu/tlkltGRIXZsaMQKNpIVNdTW1aJrypK56w63IJUukM6bqE6SlsZ6htM6Le1deAwNVYHl1GkSyeDwGG+cPIeSnWBmLsXJsSxF0yISCqEviv1VFJVQuJaB/lOYxQwvPf8y3Zt7MAyDUCh0eSqwH1PGxkb55r/8MwcPHiKVSjM8PEx7ezsdHR3uEFzOvlfeWH70owPs2rWbmZlpVFWrvoMV21vFqZE1LZ68MIDIp7nxlm1MSA9PjKdpjdVQG/ahLKT4+IPL1fWKC5eU8n8DWKbJ6Mgl5uamSSfnSSVnSSbizManyWUz+AOhZdx3Eic1xKkzF4jVtxJduw4r0ghCpTh9mki4BqH7yle6q3A+neLNl17CsR2SOZN4QTA9M8/8tEtQ2dbeuWKByGazHDx4EFVVmZubY2hoiFQqxZtvvklrayuRSGQR4NHNCzYzM0+pZOELRqjp2EEoUotZsikWS3i9xrKjPAyPjPA/f///cK6vn1JC5bXeMU5eusQrr73G9Tt20NTUuAgHBMNDwxw/cYLTp0/zxBNPsH//Afr6+kkk5hkZGSUcDuPzeXFwgbGlYokzfRd5+rkDKNG1bOpcz4ETlxC2Tf/IKBs3bsDQlr6clYwH8fgsHWvbqauvw+/zo6gKUsol8AlczCpvvHGQR7/7NP6aesamp/A09JDLWhw9dZ6aSMilGisblQupC8wOf5fzB/+J44ceJ5MyGR6b5guf/wKtzTFa2jpXTGLbtnnqqaf48698lZGESVENcezsAJPxed48+CbrOjvp6lpI56uqKi3NTZhWgYOHDpNNTJKYHGRk4BwBf5BQtH6JcVsIQTBcy/neYwSNGZqbfXh8EXzRDveaBbfDIiO1u7vH47MMDQyRKZqs77mOkr+BvCmIhrwUSgWCwdCSuVUsmQyMzBCqbaMUaMOoW0cwUkc2myMWWUTmW872IaXg2KlBTg3OUxQ+Jkth1GArc/MZdB1iETfp4OJNbWpmjhPnB5lN5kjaBs8fH0Gi8dhTBzAMg00b1i1RGcORCM8//wo+j8rU1Bg7b7gFw+OjVLKWzPMV73D5c0UugVUkFAqxf/8BpqcniMdn6Ovrp7+/n7e85S5CodAVFkfJk08+QU/PNhRF8PnPf562tjZ6erZW+8rNWeKafU7NzfKFHz7NQDrHs8cneG4iR58UKB7JvrZ1i0OeLrtwXRUOMTY0wKE3nmF4aAB/IMj8/ByO4+D3eZCOiaJ66N5wHbv23EldQ7N7ZHUsnFyK5jovejiAomoMDF9gXXMD85cu0tTUieGrqeJzJDA1PctcIsP69a3MzxqEQn48polTsF3CVrlU+x0fH+fBBx/k9OnTVbAkuKcQv9/Pc889x5/+6Z+yadOCOuHYDpZlMzo2wvr161AUlWLBJJNO4fHoBINe/H7fkqP6G28cIpFI4DU8FCxJZ2M78fELTE5OkM0tJcsYGxvjl3754wwODhIMBpmNx8nls6iqRmNDI4VikX379vHgb/82m7dvBQvSuTx/9/0fElBVLqYGaYqFGUzniPeeJKuGuaV/gJ6eLdUy0uk0//atb/PSSy8Si4bJF4o4UhKLRgiHgvRdHOS3PvMZdt9046JRlOieIInkHMMDo5w4cp7N+zoIZdIcOn6c67ZsWADFSsEPHn0Ub+4wllVA0SQXT/49qswRtCX/8e/fp6u7m0isASG08snIBVkWCgXmUkmEUHj4hweqZWezOc739XHPPW+tIvOlhGAgwolXH8cuZcEbJlbfgu7xMTR4hsa2DahV+M3CYtfUuQ2/2kDIM0tm6k3OnjlHMTFKLNLGtrs+jOELsTgdtJSSc6d7yedNVF0hY4YxpYbX43DufB91Dc00lglYKq+LaVo4UlBb55LB6pqglM+haqvpMIKSYzKbKSFtB1urxeP3IIs2RdvGlqurcZOTcRLJLIlUgSffPMb8/Dzh2iZqIhFCQZfqrJLzvzIfe7ZfRymfITE/zlOP/Rubtu0lGPATn5lm+46d1Q2r0v5UIkF8fp58Ms3Jw0dIJlOEgkG6NnXTvLad5tYWdN2oLtqKotDQ0EBtbT2Tk2MoiqCxsYGHH36Y3/iN31zJYl3WYoaGBnniiSeZnPwnUukUJ0+eYmpqamVXSQVFSM6NzhBP5qnp3EDWgYCwyWbzmEIDaVGlFLyCXJkQFkmpMMfFUwfwBRsxtHrO9l5gJj7H9k2dNNTDwHAcn8hz/S23VTEGtlmimJhFiDDCdLCExY4t25mbnqCluY3s1ABG3folC9HY6BjZdBqfP0CpYKPrKlY+w6kTZ7nnrt2kE0lCiwJue0+d4pmnn0YpI6DVMhJeSkkymeTNN9/k6NGjbNq0gMadi89iFYsYhgekm3ZaUxVKZhFV1bDMZbmvgMmpCUrFIpqqMmVnmZhNuht5ecddzDVXKplIR9CzZRtbt26mt7eXRCJBPp9n164bSCRSeAwfTc1NgOtql1KiKbC+pYGpXI7mSJDt6zrJTY+R0gIUrbxbl/Ju7fP5mJqe4tjxExSLBTweL5qqUigWkY4b4/bmwYPccOOuMhIdLMdE8wbxeBRy2TiBoI1imKjBCHqkiYmkVX5R3N39/g//D44cOcjQsSfxZffjrQkSVovcsSnFofMv8vd/1s8vfPSdBGq3Eam/jcorn8vniUZjGIZBPB5H1RQs06ampobDhw9TKpWqKimApnnIpwu87R0fYOjiKTRdpVjIkk7M0XviBTZvuxXDWIzhk3Ruvonx4UFefvkh5mYGaG1qJ+axGB7rRXgMtt3+C2i6twzyUBB2ke4YjBsGJ88PUMhnyWYyrF+3noBHI+KVCOkgReVVsJG2g6EJDM1l6UGAVEF1CsvsT+6YpJI55uIZPIbHDcamDLoWroq4GuQrUzDJFwqEgkEamlrp7Gxn88Yunjtwie89+jjXbVlPfW3tErvgvn23Ydkm/ed7UewSp08exuPx8PyPDlD80Ee48eabq4vLyePH+ZeH/pa2zk68oRAXzp6ma/0G5nNZTp07x7Fjx2kNRXjbAx9ADyzYU9/+9rdz66238ulP/zqG4eHll1/m4MGDNDQ08vM///5FWE5Xy0inU/zRH/0hzzxzgFKx6IaoCUgmk6vagR3Hxi4VqG9qJB+IoRk6jmVSY45Qr0mkLa7J8n4VM5jC2q6d3PyW/0Zbx2byJYvurbvo2rgFzetFMcLcuPce7n3vp6hdFHvomDkKuQSRhnK6FAekdPD5vNS3bsQxlwLshICmGi8ROcfg4BQzcymyuSzZZJobd25gY6iI31i6qDQ2NuJIl8OuWCySz+fJZrMUCgUKhcIKT4YAvM4cpcmDRCNeUNwga4RNLBqE8ZcR+clqfSr3IEHRVCzpYDoWNhLDMAj6A+VT4EI5a9e28/tf+Dx7995CKBjAY2hEIxFUVeXs+fNs2bKZj33so9TWxlClRCgSoUrWyyk2tDcymXWYyliMz84xlsmwL5xiW0t4yaS3LMsNRDZLaJqGZZkUigUURVRVxaefeppCYcFTKoTGOs8MdXqRgXiGeMmH5oBdyHBLl5d3tKeW5CxXFIUbbryZ+37pc6Rq72c4HmRkzkskFuM9dwboqc9y6rUfcvj1g9UyTNPk9JnTKIrLP1hJBeNYNrfduo9sOrN0PISL47rz534Zjy+E7gsxMRUHIcgWSqTSCcZG+8lmk4u8OILW9k56rr+ZrbvvoykcpJQaZmJqglwyxckDf8+LD38ZqxxSJoD83AQhUSQUCuELhPB5fZQKRWanp9jUEqXOSLLYTSRRMIpxAjKLI93kj1IKKCQJT77merirF7taQLGQx9EkHq9BJm/h2ALDUNzDgxSrekpjzFOYvEhbcx2GrtHWWE82k+Mdb9nH77y1hfBiPLdwuRRramsJh6P4ww2kMyVy2SKJRB5bKjz5+A84d+ZM9Z5Wf5TbvS00vTmA58IQ+qmLxF47RfCVY+ivHKVlNsOacBSrHPRfUUlvuukmrrtuB/v27XODzB1JNBrjK1/5Cs888+xSG66AbC6LrhkoQqBqbo6vaMTHTddvXbJaK5bAooQtUuyrL9AUdnPl2zhIIdnX5OUjbR6EcW1AhyufuIRAVTVuvdONWyqVSmQyGRTFDYSV0iYSXRTCUv5wbAszM49jOUjFwJaUiTIljqZiyqX2MKQgbEhuv2ELF1JesiWXoWZyYppsUmNjrJ7AsqRtepnXT1TyJS2qc2WgFwfdIiSoOnV1TaRVD47tYDvuCck0SwS92gpEQGWISqaJUBRKThk1ICRC0xgeGV2yq6iqyh133M4dd9xOoVAgPjODZbvkqLph0NjYUE0d494HYc3mPTetI+FTqAv7qQv7uGFDJ3mzldLECRTFrrYL3FxGTz35DKqio6qqe+oTwvV6SdBUnZn4DMPDI2zatLE8yBKvnaSpViMTbGTcjlNTV0MyNUEhNUu0rhYhlhpfBaDrXu7/6H9nfPyDeEQBWcoRn5mioyaC7q3B0ULVE6dpmvT29jI7GyeRSJQ3DrduuVyOQCCwwn4ohMDvDyKRbOm5iQ3dOxgfGyGVFQhhEArVlmnHFk61Eonf72PXnQ/Qte12kEXS6SwXL/QzNzNJ9+7byiqmgsChWCyRyZrIqJdb9+4lm0kRq4+RS8zhUYrknfK1ZeCsQFLMZylKD4oDtnQ5BW0jhKlHUKRchFx2513YJ4gPnyOPn7aO9ZiWxeDAEO2hBE2helZLrNygZti2vone4QnePHSUaPBmVC3K6RPH2HhLtEJCvkSEcIOxP/4rnyKfz2JbNqOjo0xNz9DW1kZXd3f1utruDu75g89QTGaQqovU96oajqtq4ItG8IWCK8YDoL6+jq985c+ZmppyPY19fQwMDNDW1rbiBNXQ0MBv/OZv0tffz8DAIIbHIBwKsfvm29y5Ublec0CqaNJHQXqZUwLYlkSTFg4WF8biyB0bgTJL91XkmvJxVXrQ43FJNK8uChnLYG5yDkVz1bhSKY+wigTUIpE1WwBRJoSV2AhCsTouXjqP46kn7Bcg89Q31jE9MY4WiKHoS5O+eTweNm/ZDNJdpCoLiFKOv3ODOWuqdZcIfLFGJvpOMq0a1MTq8Pr8FEtFxoYH2BLxEKhxWakr4G+QRKMh7rp9L+09t9I/PIWCzab17ZjZSY4deoMP3v+BJbp/pX4+n4817e1X6KMyhbviI57XOTt2ibGJJPPpLNPxWUan59nolLAshcWBE4VCgVgsiqpp5YW5kvbaXtI3/f0X2bSpu1ySgqnoNNU0Mz46Q6g0hhkfJpOYpjbgQUQaVvVBu88TtLQsJMKrW7uNKgRAiOqp05GSVNJVo20JQqiomoGuCvounGNd50qDvvsQ1wakajqqprOuazPrujYt/HHF5a4KpqoqNY1rkFISa4T2rm0sj3x2gfyC+VSeC+N97Ll5NwiF5qa1nB6fJD6VoWnn3mrbBQte3unZHDE9iGEYmJZJMplC2h4alhg43LQuimOyd+dmhjIGBcvB4/GwpquD0PwFNGlhCwttmd0mU3CYSNrMJ3MkUhlGJmZYs6aFXC5LiRYul35bCEEoHCIUDiGlpKlltSSF5U0hGsEfXT2C5EoihCASiVQJnTdv3lzuz+VHR4EQKl1dG7j33rfxwgsvUCqVCIfDGGWOzWorhIOGCrbGYHwWWcohVA1pa0hM5pM5LFsu8iJcWX4mOC5TaNgO/PNjL2OpXsYHz6NoXupjIf77nU3U3vxWoDL9FRTAG4xStGy+9+SjlIRBYmYSqWisbwgRDrn5hxbL+vVdvPzSK6u+DJVFTIhFE1kKpG0zdOpN/uC7f0NDUzMBv47q8WNNnuMPP/1hVH1p8K1wHD76wPu4q7uOE9YahC9CTTiE16Ow1iyy812/jKIsZ0q8VnHrphs63T1b+drv/TljToB/nRhkcmaaRDLNPR++ExsdFpXQ1bWe557ff8UnSyndU6gApIODxOuPcHxgkEeODJKemuSxN9/k5ht28L/vvxVLuTaGn4pRfbU+N3SdT//KL3Hq4BuIhnVMFzR83hBdaxvxzBxl++YO9GtMWfKT9aZYFDsvKCHREGgSxgZH6U17ePPct6EE1+3YQmx2AH+sHemtWwa9cNCEzStPP8zFBNx+6x76L17i+PHj/OI9W+navgvFW1kM3OXFthzy8X6ePtBLNpPB5/cTi4a5d1cbTjGLWOU1i9Q0MP7KWTK+Vjo2dDAwNY04cxGv30uktqnqmPix++GniC28ljKEcAPXH3zwQR588MHq4qYsz9ThaDgCpCrZVRNly9CrvHbmPO+89x28+OyLtEdVEsMbibZvv6bh/5kg5w3Dh17XRceaRgIBL+9/51tpaW6ic20zSdUDakWBr6gAEkf307yuh+HRSc5dHGR8coZcyWGypDCWFiCWDr6rxqrVE9bin8r3S2PjJELROTZeRAjJTwCc6QAAIABJREFUxMQw58+dpu/sWe646x4iHVsX6lO5Q1GxbI2j4xbJdIo7rl9H19o6spl5OrfeSGDNFoS4PMPKlcU9zTiKhr+uizvvuI1CNsnrr79KMpUmUhPhyFQB4Y8s2eMXnyqrbaz8KAt/cxvgliEUBdvXSMemrQhDIDTw+r3kpcrro0m8kYZV6sdifKX7a2XHrcQkLhLdMPjQhz/Kp3/1k+y5eQ/33HYT7333HdS3tnL9rfewc8/by4SlP75cKfRjcVUX/l85r0j8TWvJ13XiDYaYmprlyOkznDpzlpaGKIG2bozlNJJCwVu7lnfe937OnT3JwEAfYyNDxOMzzJd0VN2/5FqJguYNsmHrdhSZ4fSpNzCUPH3nT5JHQ/GFWA26Gm7dQFNDPdMzs8ylc3SvaaI+FqGhuRFvYxf6KmmclsuSsa/gpa4cwnfVflzR34v+rVT1qb5jinD5OhfPv6WLqEBIUNCItXZz33U3UDp5hl0hP+FCjqmpWQId25H26oQ6K9p+FcDkTxRPIGVZdcgVmBgfZWTgIq0d62hqaCTg97lewDLGSFaARhKk4zAxPcPk1CSnjh9nw4Zu2js7aaqrRdeNn2QjXlKn+TMvkpOCi3OCUCiA1+OhkM8RCYeodeYINq5FDTchhFr2GroZUkuWS/IaCwdxpKBYKhEK+Mug2J9wl5OLO9fNFz+XTHH6xHEymQw7d91MMKTTVNcKyuoAVIBcNuvi1tpa6erqqnoSF8pYKCWXyzGbSOBYbmZKVdPxGBoNdfVVlW1lHSVnz55hZnaWm3ffVCbbLU8uoayAqVi2Tb5gMnjpIiMjw1x/w26ikSDeco76n2QI/ythLRJIZzJkc1l6jxwnZ5rcsPtGasIBfL6AWyexeHtzy3Ism+l4HE3T0FSNdCZDTSxKIBCo9lUFKybLdq/pmVku9vczMz2JPxjkxhuuJxKJIFFRF2H9wJ0z2XyBRCrLhQsXmJkaY8u2HTQ21FIbjbp2sZ9kWv2EfbUSeG1y6PAhNFXjxht3rz4/ymLbNvG5OH1nL7C+ax1N5QiApc9cUkts22FkdJRILExv7zlqY1E2dm9wE0kumC0u2wM/E1VRCIECGIpg/8P/SmMA+k+f4Fd/67MoiopcZMxfEhmuqrQ0NfJPX/8yL+x/hm3XbedLX30I/TL5k36sOgHZ2QkibetZH24ikUpjOxJ/IIzH0MnF59AjdfjDS++ajU+hKK5nKZ0oUNfQgt97eTvfNSPnF2uxUhAMRpiPz1IYPQuFDBSuo25dBV+0ujoM8P2Hv8ujj3yX2/ftoeMzv4thLHZILL03EAi4L941iiUdhocG+LMvfg6PLJCe/TjvvO8D7viJ8qlm0WIkpURTVUqFef75b7/BxMQwM2MD/OKv/Pr/FRVmuVROA6FggPGRIZ763r8h7DzX92zC29BQOTIsvafcGlXTaG5qqn4fi0VXPt8txAVYOpJwwM/xV/dz/uhrRNo28ZY77kJIgSIqYVtLy/J7PThWiUe++w+cP3uKT37y19j8/o+sYMX5r0hlnlTNByyYUq4kr730PA994y/wew1Cn/sjNvVsu+wy4iB44UdP89z3/52endfzm//Pl1Zcs6Q4CaqqsKa1hWce+TanThynZ9ceNnVv4FqVwJ9prKIiIDlxifvuuY5Hjo6Tz+XwB0OLrd9LpNLJn3zHHn733m4upjJu5kl5uTjCaxeJJG9ZlGbTvHxxnnMDk4wOD9C5YR1337KdTo+PQsHEL9SqRalYyjM9MYwQJtt23MrY6AAT44M0t3TiTsTlZUAmleKbD/01RdtB9XjQVA1NNzALeUr5HPe++91s2b59WXskDhaz8RkC+SwdDWGe/uFjfHzL75Tz+K8+Z6SUTF28wAN7Ouje0IhdKiJ1fdmzf3JRhCCbmOfeXRu5aWMj+y/244YBueFXq9cJMsl56owCn/yN93JxcB6wkXJpFtD/KyIr1kHJ0498hzu2t3P9xlZe/dEBPvjx9f/1xy+KORQCPLrCz7/jDvQ9HVyYSgFFHOErE0asfr/f5+V/ffJ+DOutlPwtbnrtn6Ikkym+9rW/JllwkKU0hqbj94f47Gd/vRwPu7RmlTZdt6GNv/3CJyikU1BXy2I763JFTMNhZ2cDOz50L0rNmsVPK38uKwMAh1w+x1z/KX717Tv420ee5Pa33E0gFHG1savIz2ThqjS+VMpw/S03YmoWm9t8PPPoP/OOD3wM3QgsjkeqTjDbkVAoEr+QZvTiIexwI3KvRBoOQlEuu4AtRkqvlMoCIzB8NVwcSzCVDTA2kyKZsZhNOjz57Et8bFeQurVlhHr5+ZZZZHjwHKoOoVANjc0dXOw7TkPjGlRVY+VyIimZJk89/jj5fN4NegaEULBtm3whz5bt26sL12IxizaHxwaIGiolW+N4ZpzBiRG669eiLk7zXW6rlHDp0HEunj7OfR++icf3H6a94x70jR4Uz5VyU11dqv0sJZruxfBHmC+pzM2nyGRzBILBpfTty/rg6JuvEXTyhJUSAzMZLBtUZWXgsNvVYsnvi79b/v2P3Y7KfxyYHJ6lq01w9niakWnBeN9FGtatRTH06ouyuE7L61OBryxu9AIsVoIjKToap6YsOpwCWqSeuZJCjWGDstSQtmAqlGSLJc6OzdMmx5mviRFFoElZ9Z5err+upfFSQDgcoqWliW988SvkkzMEvT4+9KEHyvP38v1mNHQzfPJ5ig50hmuravFy8K2UkC1YpNI5Gr1eDvXPUbMtRSwWAJTL1ldKSSaT5o3zo0SNPJnMFH1nT7Nj9y6k9Fy1ndcUZP2TSD6b5pF//TIqKRqb15JJJIlP9ZE3S7Sv377sOOz60A/91T9y8De+wKUfHGCyd4SZl89y/uHHyQyNUr9zK2o5Wdpqkx0pySZSDB09w+SFS8wOjZONzxFpbkCUPX+KqjMTz9A/nSWbTtPVVkM8mScYCbN7783UNbcjlIXO1jSd2ekJSoV5VM1k8MJh5qYHqalrxx9cGR8mgEw6y3e+/R2k4+DYbv74QqmE7vGCrnPnXXfR2dW11N4jJXMvvMgr549xaD7B8USanGHSdOQULVu34Q0skJy6XSVJHesn/fhROv0tGFotmwNbSB3sRYsF8bc2/JdPNqZp8vyz+3nj1YMMj0wxm9c5e36QYjZNU3MTvmBoyRhWjLmpdJYzp0/TtnEX9V27mZ5N0rZmHcVSAZ/Pt2JRklISj8c5c+YMly4NMD3tBuguv/YnEYlAOJL+Q8dJ9w5TH4rS0L6G9IU0w4eOYyJp7lqPoi7Mp9nZWRLJJIODA1iWRTgcLj/LlSV1kmAJB9VyeOzMWb701HM80T9Opmjz7MA0Pzg1xFQ+xc41a5aRx0pyxSLf23+A7+3/EbZmMDY2xX+8dIz9r79Bc0szddHoyhCbRbLa4rq07ZIL5/u5dGmIJ596it6jR1ADNagtW6mtiRHx6SAltTWx6lyUQMmymUzapEsKiieAXtNBVqsnV7DwGaLcjkp/waE3TvNXX/9Pzp0dp2NtDd9/YZRjJ4aYnoyzcVMHqqau2ERtx+aJJ57kH//xnxmamCJU28qR3hGOHDvN/HyKnq1bK+FLf3C59l+dngxIzc1QKrkhJQuU8Au4KRCEIjV4fJ5yHmuF+ZkRzNwcRqiZ/uFRCmTRAyHs3FyVzaXaECmZPt/Hub/5FpmxSRq8QWTBRV6nRyfp/btv0fLW2+i4ax8AhWyOubEpLp09z/y5ISYvDuEA+VSa/EQcabrkAbH2Jj760JcIRMMoQKhpLaHZEr7xPtpb6/no++/ki1//HobhQQ/GXJafRX0shMqmrbsZ6JekktPksmk0odN77GV27L6HaE0ti3cViUDXNUrFPB5fAEVRCPj9GJaNqghKs3GMRRtWec/CskyGnn2G+zI2J0MKYZ+Pbl8d/QNDpPv7iDQ0LhoRgXAczPgcAY+XrS17KMgMhl9hYOQsDWJZ2FIZ43Xp0iWcsgNkdHyMmfgszU1NNDc1oaoKa9vb3cwSouxMtB1OHj3JzNQsNZE6inmN9tZWzvae5MY9e6htbFlx4srlCmSyRe5+23uQSBLpAHe/Yy2mlUKa9hKSkHQ6zfe//yivvfYaExPjDAwMuiBJr5f29naaGpvo7OzkF/7bA3R2dgBQzOUxCwXsYhHbNLFMC0XXEI5DqVDAsh380Qg1zeWYWUVSSqTpffQA3kyJ6RGbTCqBJgxS2QS9P3qJrW+7E5/ucR0xxQLf+tY38fmDzGYyhCJRrt9xHdLwc7j3DNu3bOH2nm73Zaw4l4SNicJjx3oZTRTxhut5IZ+BYBQnZ/Nwbz939WxjS0PNonkCp8/38S/ffwSP10tjyx0MOrXkRJL0/CzPvvY6a+obaFiU4//8+Qt89zsPc+z4MReCI12jeDQa5V3vejdbtmyio6MDf5n+TUrJhb4LfOYzv4MjFQKBCIquo2WneeXFOV559im+8bX/l2gkQn1DXXVhzuQKFEsKfo+OWNODApRsiVW0yeSKxMKVdMeuG+OVV3qxHQ3V28zjh2waW7eSzSY5fuQCb3/XrRiGVtVgKgttYj7JI4/8kFwuh98fpndoBiVYQ66Y56WXXuKOO++ga0P3atakqlyVLGNmcpRnH/47ivksSAfLNHGkG6enqQq67nLP3fbOB+jaugsQ2Fae4b4jGJaDH4muCIQ3Qtu69ajkcZa9XIoQWGNjOHMJ8obrTrWExLEspijg1RRGXn2TNXftRUVhbnyK733hLygmU5SSaXSvl3w+j+bRUaWD6lEplixmU0k0T9mwX95VmhvriUXGmcmlCPl0gobA5zPKEenL9B8pCUXr6Niwmwu9zxFtqcUTCGF4I5w5+Rybtt1GTW3zEmOnAAwB6cQsum5g5rJYtoWCIBLwEY0uNfI6gGI6oBsoRp49Xh95qwANzfTs+TCaumz0BCiqQt0t7Zh6nOzpNMJJkMtbNG1uI9LhY7m88OIL/J//n7r3jrPrKu+9v2u30+vMma4Z1VGX1SzJtmy5d8DGGOM4BPDl3hCIgZCEG0jyQgIYQq55wSGFUEI1zYANtsFgy1W2bEuy+qjMaKTpfU5vu6z7xz5TNSMJDPm87/P5qJ6zZq+213rK7/k9n/oMt99+O02NDQihoCoKXV1dHDp0iF89+STvfuc93Hbb7eiGhkQgFYX+vj4028Z2PHgDIXSpMzCURpxVracStR3P4vEaSNvG8AbxWS68GEVBUzRMy8JjGIDkzJluvvPthyiVCixa2EzrksX09fWRTKVwLIsDBw6ye/erKIrCxz7+URzb4YUf/5STu15GG08yMtiHo2jooQieQh6nUKA/m6Nl6xbufeCz+AIuH1tXewfDfQMMjYzQnKgjZ2WxyyZFyyRiuRTHk1MrFBYsaGbXSy+TV72ECzYju14hEY9RSibZtb/M+sUN+HUXiC2EQEPl9d4BekaypEYG0X0+UDUUoZIZ70WLxmgfHGVVTXxynzhI+pNjWI7N+uXLyRfy6IZBOpOmIRjEa/iQjjP5fdu2+eQnPsPatSu54frrePHFF+jp7mL9hk0sWbKUh773EMPDQ3zgA+/nHXffhZSSQ4cOs3ffQQpFiaa5F6eCRHFKaLaF1PxUVyVmlANzpI3l6IznCjhWiVAsTNmSFPImmXQaf81MvF+hUKZQtkmlcySqvZRtHY0C5WIJnz/EUP8Y0Uho2vy6WuLJEyfIpNN4PF6k4nLdSWkzPDKCZdukUumz38VZcl6NK5McxSzmMHQVRehIw0OpbKFpLs+QbVtoqoK07YoLUjLS30dH236qWuoQQQ+2aVEYTpMPFDD0EqptgTIVKRRAw0XNXP6Jt9P2ndcod/SiSChZZRZsWU/LpjpW3LFj0jSpW9zC9R96F9///JcY6RrGk9bwenwoEgyfB0VRCfsM0Dwzhi6EwOf1oAtJyK8QjYa5bNtF9I8lZ/rcJhtIdE0QicQZHx1npNSDEAaBiIexkXGG+rp5y90fmQzfiolguqoS9HpRVRWPpuNIjUKpjKKq6MZ0h5WDjZv2oNdoiC4HNW/j5EqY5RH8EYfE4g0zu+QOBCkkdj7H8OgpTNXAzPeQ6bXwrQwT37F4hnouUKhvaGBkdJSeMx34/H5sqZJOj1EolEDaeDyeSpK6QOCg2TmWLKznyJkxdOHDLMDY4BDXXtxKVdCDepaTy01H0XUFlSIlu0g0qGNLSKcLSKni9/vwelxtoampkdbWpezduw/bMnlp9ytIB7w+LydOtOPxevH7fFx73TVMYNHSg8P0HzyKnRzGq2qISBXmWJpCdoyw4aE5FKZUKGBb1mROYTGdQ1d11m/cSG5gBIGgIAtc/uabObLrNZcrvqIJeDwe7rjjbaxcuYrPfvZ+vGaOpYkVDHa209vfiz9ew89+nMPQde644214K/mqXjvJW5q87IsuosvWUKVC0S7RUBXFTvdipkaRcorSR0gY7TzAbZdu5sRIjrFUikwqhT/gZcPG9RxuO0Z+x+WT764QgmWtS9i58xnKZpnmpjrKZZPXXn2VZ555HkVRWLRoIdWJeGWfQ1vbMb78b99A0zxIp4gUmmsxmZaLm3RUnn/+ZdasXT65grbpwPhxvFaQQKwRRYChqZiqhSyMYY+NIiMu24rERsFk0wLB+ICYLPPmZk1k2NqUw0ydQdAytd0r87zzmZ2MjyeJRqN4vV5My8aybGKxOKquYV8Aw+15D65yIe/W7JOSTC5LMpmiqqaBkeFBvF4v0VgYx5Eomu5iW6RDVU0D4WA10XCAbCFDOZ/FYyh4NB0lkHAzwGcAgyXj/aeJr1pJru+XaI6bB2kjiVZXseLKDfhjbp1EIQQoEEtUow0XaQq7uWC2dHmoFMf1Z1k4aJxt+3s8OkePHCMW8blO82KK0bFRiqUys094IVxzVVM1tmy/lY5je5CKSj5XIJ4IEQ5EcBxrqhCuhEP791MoFlGkxNB1LMtGUxRsxyGdzjA6OjbtAS67RD49TGF4lJDwMWK7xWnD3lpqlq5H0fxz3zuql97Tx+k83U4mbeMPKhS7ehi5KEJ8x0yH9uWXb2f1mtV0dHQw2NfNcy++giMd1q65iO3bL6O6qppoNDoNwOggFGiurSJXUIhEIuzfvx9Vh6X1Qfw+jdnRECEgHguS7D3u+vaCdTiqjuPYGB4PHsM/2UQIQTQa4XP/9GmymRz9vT3c/Ud/hKZppFJJ8oUS4UiESy/dNqN2o6KpOKU8E64JBIyXcxSLBRxvkLjjkPB63bqcLmIDQwrS4+PoQkXaDppQKBULaMEgis8zOY7p+2TVqlU8+OC/8MQTT3DgwAFOnDjB6dOnUVWV7sWLWbJkCVdfPU6t3w/CYWksyJDP5uUxsKWNlA6KUKjxefnYldupbWg4C7h5w+omuq0wj/zHD2hobsAwDFK5ND4PfPCKRhriU5qzoih89KN/STr9XrrOdPPaqy+TqKmhXLawbYnfH+Dd7/kTVq6cYkKxbRukwLHySGyEoqAisKWDWS5iOQovv/Qy733vPW6fAENX0NNdmMUwsroey3LzNIVjEncGUAe7YfGKypxJhFNi/UKF/af8pIvOJCDa0FXWLYtQ1ZQ4S2+SUqJrOmvXrmV8fJx8Po9QXWokr98Hqpukfz45r6mYTY+zaN12kuPjBEpFfLk8tm1RFa5B13UCgQCqouINRSs3hIqiG6xator9h14mEgoQCMZIpYdY0tJEZPVVqHLmieqgUCqbdD+/ByyTIjbStrGlQ7p/EN0I44lM+XmEEEQbatjx4Xcx0N2DoyrkigWk46DrbvK1YkvCXj9SETNMOUUR9PcOsHHd5SAlfq+X13a/zs1XbGNR89nKqQv1UaipbaGmtgUXPOf6Q1yUvjFtUwqWtrbykb/9e7LpDD6fD7/fj8/nQzd0PB4Py1eeXfRS4jK2Wp1JimYRv6qj6iF0LYhU5omqlSXDaZXMQI7y0ABd1KOVsiyeVe1GCJerrK62lrraWqSU3H7H22d8PnvNAcxSibYTx+kcNSl2dtDf34/QYaTQymKhgXRAzMzRVFTQqxaRTOeRZQfFKoDjUCzm0WM+lGl+TTcfLkokEqWxqXHO6OGMIIwQBJqbGGuqo+zAsK7j0w1MaePzLCFneFACAVq3bcHw+10KYOmmWnt1D8JysKVDXlqYlkXXkWMuPlA7O/IlhCAej3PPPfdw9913MzAwwMDAAI7jUFtbS2NjI4qiYFaAIZZpkvdG6M2NovoEtlCRUjBYzuIg8cVmk/AJyo7k2b172bBxIy1NDQhH0js2zLGTHdx85VJURZ1MHxVCEAoFCYWCNDY2su2SreecK9t28Pv81NeE6e3rRlc1spkcfn+4wiqi07x0MzfecgPB4FQmgBQKhj9EOLIQKaBcttE0gYOCoQiEN+Z+D4FERVNUDF+YqhovydPjKIobPVeEjlD8+KKxOftpeAz6+vqYSMD3eN2CzOVSmVXr1rBs2bKz2s2W87JDrNhwKcHw2eC7OWUCBC8UIi3LMTr2o3u8ZDM5ypaFHq1HmQP/BA5KqcRA3yhpTaNQLGCoNvh9RKq9lKRNNDADGYrh9bD9rTf9DkF/iUeXvLbnIHe+6Wq6ewYZHewHp8CFZR0KFIW5bwUB9Y2NvPXOt52j+VR0ENxbMFjbhLF0JfljL6KrIKwylPIVc0fO2StHmIyf6sEb85BILMR7qofBgJ/UwHgFJzff4889PiEkEhXpSNatXc7j3/wJwyNjBANBPIoXf6TGZS6Qc0e8xrI5jp4ZIZPNMjA4RKI6Qdxrssnux1i03tXm5k3orkzNHABJgeS6O27nujtuq8xHhcu94pqUTIu0TTrOXb9N2cohRQRHSLdkViCAV8uz5upLcM6TP6koCg0NDTMSzSfErbutUi7ZBO0i4ew4yDAoIByNQKYfO1uHI/wzqnQJCYFAFf3tvya4bDNCOhRLBYrpDGTThOILUD0h5lvE862hoihcedUOHnjATzqdRlVVcvkcWqWoraoZtCxcxOaN6ybJCickm0rx5HPfoTOvo2kadXV1DPT1sKMmyzV3/dnMS9ETwFG8nO49Qlv7GfKFAkJRaKz24Q82ItQpbh73YnL38uVrWymPjHIyZ1LoHsHSHFQ9QGm0m5Bv/oK90+U8RIIQCkfnBIuec+KEJFTbiL+xmYOH9+P36fiMIP6qhbgF1WcmkAoETiDKyYY6Tr+thud2Pk/UH2H1smbevSlItuRQx8wFm5mHeOGSy+a59tLV1CxoAiR33XETYa9NQL/wtM1zbZwLDuELYILwRDGIrtnA4FOvoZQhqTgEHFEhGnRhQLMlnc5RyJ4hUzARGzex+8hJNrcmWLr5Ci6o2sC84hbOMAIRrrj+Fr71y+coZDNcum0Lew8eIC9CaIH5X6r6eJhjp0cxbYuwV5I1JYpl0XmwjdjiDdjMlXI8a2rmnMMJXNPUus+sxzezrS1sFFRatqxke/5yTozCcFmQqGsk3z+EN9qD0eibSX10Qf2YEsVxz0E9FCHVM8yy5DAHX32VTLHIyuUref9VLSxa0ISiztzvDuCNNtG6bDlPHjzArv4zWEKntrqav7xuHZbmujHeCBwkGo1w3fXXXvDYKgYzyWSZnuNHeLUvw9jQEJHqBG++ZC3NVQkUdaIuAQgczIrfzCz2kbOH6RvsJRaLgVpDJjlOTOrTID+AcHCAupoaNEPgyBixeoOsZdMStPizP72FBWs3nJ2gPYecew+9gYkzPH4u3n4HB44OU5RgaAEU3cNkvH0WjMvXvJYFK1P4xlLcdvO17N1/lLpEhJXXXIbvPBqfnPnbpAN7LgkG/Nx44/X4Q0GEohCJBLnp5hvxeIw3ANmco08TgMFZQMI5vwuEm5aSefPVyEwOqUgGdJ0mBMY84zACNfRcfCdX79hE0+JmdplVXPLuO2hY1DLn9y9UJhA9UvXiidby7ne9m+eff4FtWy/B6/exbN1ml2JoHgvW0DVqIwYhb4yaSB19o0VsgjTV34KieebQauXU0s3IqJCVSPDvtioKKkJK1GiMBVtuJHuyi5iisG3jKvYe6sSQq3HCdWjMTRU83XSdgAApyszDxBESRQq8gRqSkQXUtgZ5+0VrGRkYRAtF0RuXoNevPkvDFDj4aptJLFzJNukjtGktqVyRSDTGou3bidY2ngUh+G1kRh+dKe1bCveqnKi0M3tj2qUcaiRKv6lTSCfZtmEdJ06d5pVDx3n7XX+EY+WQMgIobhBHSkIrt3HZdQ6hhqMM9w8SDoVpbG6h5uJrUbyeyec4AhQpEFJhAB+LL7uS8uFutt6wgn1tx6mO+GjcdCV6MHpBB/YfJMkapvBDxw8f4tnHf0HD4kXc+rZ3TEYffp8yNjLCP/7d3zI4NMTnv/QlmpoW/PemlswSFxowzt/93d8yPj7Ol770IIlEYt4+TWThkyzT8fM9ZKsV1lyzGc1jvJG74w2LlG5Bki99+Uv09ffw5392HytaV04vZjCnFPJ5Hnzg8+zZs4f//bd/x6aLt86/HpUk7RdeeI5/e/AL3HjDjbz9j9+Fz+vFEa4p/ftIHP5tRUpJqVjku9/8Bs889yz3fejDbN126TnXMJfL8oOvfpme0x38yZ//FQuXLHM1p3kuL9uyGRoc4N//+VPEqut5319+dAa19cTP/V1kUvN0JJn0OJ/6h49TV1fDve+9j3i8apr2OnVAlkZOcWLnj/nPZzp56umn8RgG0nHwerx86hMfZVtrnNCq6xG8sXJ2E9kfw31dPPS1f8e0be77u/vxaApSKNOBzfM+5beyKaSUdHV28uJzL7J/375pYNS5xXEc9r3wNDdfVMfep35FMfv7LXI6gbweOHOSNbEyl7fAr3766B/kGYVCge6uLg4fOuTyaZ/jTHekpL/rFFtaAtywrobOtn1I5p+riiuLrmOn+Ndn/pPDBx/GLM5fpPa/S6R06OrqJNXZ8m/SAAAgAElEQVRzlG2L4jzx6I8uqN3w8BDm4DGuXpHg0R9869zPwOUh3/Wbn3NRgwcndZrx5DggmC8u8d8hUkpss0iDZ5wbNy7g4J5dbkBiXhGMDQ1TOH2Qd129hkMv7cLhPDe/IvjZD77HOy9pIV7sp+3I4d/vIACE5KlfPsqKhEKk3Muh13e7sCUpzuLsExLMYD379h9hyzW3cuNd7+aq2+6hqmEBseoacrlyZUQXRj0zr1Se++xTT7JjRRUbGwyO7t8Hv4V+fcG5ilLC8PAofX0DbLtsGy8//yKWdZ4ipEKSKRYopssE1TzPPb+Tm998O8zhfH0j0rpyNfHLtyGLYwQuvf0cYzg7F+1C+zE+NkZjUxOJRIL2k+2sXrtm3vtAEYIlS5cQSl+MQhmrrg6XCmZuf4oEpCMJjNt8+KpLCPgV9Io672r6cwU0/jtEsLB5Aff98VswHItMaCGOlKjzDHxiThsbG7n37jvJ9rVTteFazpHY6Ipi8abrryBhJrFiNdTWusVEzh8o+UOKxOsLcP32HaQHjlOq2YCs0PhM7JkJK2yCAdZ2yniEJIrN/hdf5E33vBtUB7fg7hxjcWB0bAQn4UEpjZEZH0M6clKd+H28I45jkS8U2Lx6Cdl8ip3PPMv2q25BVdzooGBq3RwkDcs38JHP/BuqP8pg/zBV1TGuuP7NxANpcmPD8/Ej/JYisR2HQkFiWQU8dp7D+3azcvU6vH7v+ZvzW+UqSnRNIxaP4fP5aFjQhG3b5zD9JDI3xtDJTlatrmV5aytHX29n9aoWVH9kHnW4EiKaJXP9/MnJdmwQCp1t+ygqCtFlm1A1fdK5OfG9kZERHnv8CTKZDPX1dRw/fhxd1/B6vfM+Y0LGRscIBAMYhoGmaQz296Prxry5d8MjY2QcwSunzuApjDEsg+iBKLZp4jWMGW1s6SBssF49g++5frr3naD3VD/NY1E3qlgfdrE0v8NukRUzzKlAA4ApTbHi+Z8r321iHP0DvTz//DN0HHmJRETnN3tPUnYEwUDobJNmYjyWCzdIJ0fJDHcSXbYVzQggZnGXTTy3VCqx/+BRcqO9lMa7OD5iI3xxFCTeN5iv6Ng2r+x6gXwuR1V1YsZn5zL5AE725Hj5eJFfvjJMumDxXE+IIgGiQRWvoVQQ8G600wGc4UHKj/6U1RsWE1reyCpVx07m8Da3IMTZhYZxJEW7xKL2UyRWL6W1bjELGlrQqqpA0SownLPfkfP1e3ab8dEsR0/2UywqtJ3OMl4I0dTURCjiQ5tVSNYslxCqgeMJk8qZGKKM0HSaGqpwsmPUNDYTqKo9y/c4nzkrpeRUxylisdiMd1FKSduh/Tz/m1+zZsUiglVRfvnkCxw52cGmizei65O0Uf8w5w/mAjWuiQceOXSQhYvdQpVm2aTr9GmWr1o5ZxvHLNG9+wn27jzA/l1QH63nRPsxire2oVW1VE56AEm+WOJU95D7EiNpbWkA6XCq8wzLly+bl7DPti2kYyKEysob78E2y1gILLOAVZZ4fFMQiq9+7at88YtfIhqNsXLVGsbGx7hk62b+6i//kurKpp4L05TLZnlu59MkEjXUNzagKAov73qRZa2tbLnkEpcpYVo7y7HZ33Ga02aZmroFvNzrZX2snkf2tXHlsmZCwRDq9IW2Heyn+xE/OYqaKbBaWYzp11Bf7MXZ04W473LERQuY2TFmmaqzDnwx9e/Rvj6+++CDWEjGLIVwPMz/uvdesoN9ZFLjJBrrSSxZcZaSXizm+cY3/p3BwSEiIR/96VOcGS1wtP00O664mrfeftes+XJTN0zTRjoO9asuoXbRcqQeplwqYZaK+EMT0ciJS8fh8ccf58Xdh2lIRLhhx1ZqlHpOtPfyytBe7r7nNvRKRG4CIjGxZ4SYGLf774kpnWJyEJw61c7HPvY3JJMptm3dQiQaQyoq77n3XlasWDn3hYikvbfIFx4eZTgpMdM2z8kqCrLIs8dGuGJDmvve0oBX15mEqkiJONOLdayDeGgNzhkH5fQQqRO/IbB+M3o0ygxtW0KpkEN2ncEwiwRbr6Bw4ADZg6+TOXWGyM0349Oncb5Jt0r48OAw+XwehAuK1nUdw2OgazqqproFSdSpeTFNi8d+8TwjSRXLn6Cg+mlsCfCzR3/DBz5wN15j6tKWUuKL1qHbDv5SCmmnuGLDIg51J/HqOs2rNhIPuYVLJn2yQCqVJJlMk0wmyefzvPDCi7QdPYbh8WBWiuv846c+OQMcm88XePThhzl69CixoJeOjhP0D40i/EFSmTw+3/mpxM9/cEkXhZtMJjl86Aia4UYGX3z+BcKRMA1NTYQi7gExLR4ESIJeyGVzxHI19I2P0ZcZwJlFzSodePXgCf7i099AdzIIu8iVW1bTUFdLfaKK3v4hrti+BY/hOQvfIyvlyVRVoBkeVMNDOZPGNi20Wfzxg0NDJBI1SCnp7j5DqVTm5z9/jIHBAW69+RYuvfQyqqqqZuCzkuPj/J/772egr49isUQgFETTdIYGB3jyF79gx7XXct9HPgLTok1F0+J0Xz9lVcPnrSeXM+kZTZIa6OG45hAwdFrq3VJu0rZx9o9gHRxCX5iArIM6lkNzJEQl+Vw3vkweUZlbKSWObXHsF49z/NlnUAJBfOGgS95n2ZQdiZ3Ps+GmG2i59FKEUCgBu870k+obY4Ucw9RVvnjoIKLvFMFwgGXrVnHj//60G7mdZp2d6uyg8/QpVFUjX1RJZd00r7Gxcfbt28vtt93JDBepBMcB25IIoaCoOlq4Dtt2yKfH3ZzRiQNXADhYts3JMz2UrTzJfJCedIC+/hMkEnHOdHdjlk1031TEb3hklAceeIBA0Ed1PIamCHIFE8tyzaF8Ps+99/4PlixehKqq7Nu7l0CFqXZkaJjBgQGkqvEvX/wit7/1rWy6+GJ0TSc4rUqzRHJq0KJ7oICqSEKBOsxCEtWxKdkZdr0ueMeOKhZUKyhCwcbVnkrHjmDmC+THcuBY5AeGCQaiFHu60IJxxPTtKCS64SHV/gp2QwAlEMKzahXD+QHqlzXj0WYSVXZ0nOa/vvZNjh9pp1hwfZ+eyoGl6Ro+rw+hwLqNq3n/fe9DqC6ITaJQsgWdHSe4+OKt+AISQ3UwhIqhKkxfcOGqeGhCQVcgGlDxeTUUx0IokmDAM8Nyt22HL37xQXq7u+k808XoyChj42PE4zHisSqy2Sy6rlMulxkcHGTFiuWTcxwI+PnwPTfw+WQPPV1d5AplmmrC/OP73oZ/lmY8n5z74KpstB8+9AMefuj7DA4PEQlHiMerGejvwe8PUF1Ty6f+6X4am6ZCuEgHpMPpM8MUSw5FYVJSirQuakbTdEQlBF2BMrFqUT3vuHoFX//+Y0TqW3ixrZ/i/m5qAhphj03vWJ6rL9tMU20URZlSuzVVgwkUvnDLUXk9BqVCDm8gMmNhLt58MXv3vI4QAp/Pg+M4dHV10X6yg4cffpif/ORnNDcv4IMfvG8yKnm8rY22w4cQQDASdRPMbUk4EiWXSdPZcQLTNDGmVT46duIEzYkqUkWLQMBHdW2C2lCAbCTAkqYGROVllwjs8Tzmz1/Hs20pXLMYvt4OORvLLKJ4VEZGszT7Z1ZpKWdzpHp7CC9YQMv27TSuXs3I6DB+vw87nWGsq4vk8ABN5TKax0djQwNf/88v881vfocf/fBHVHt8JLNFvIFajFyOV3e+RrL5R/hj9dxyy7UEAu7BffzYUWzLZnw8idc7Ed2URCJRHLNAIZ8hGJqJjBaKiqI5OHYFIioqZeoVDVXzYju2ywMlHRwECibrGrzIrMFYqUgsFiEQ8JMbH+DOq5ZiDZ+GZpcjzXEcfviLp9i15wB/+p4/4dqrd9B+/ChLWls5fOQ4X/v+w5w6fIDh0TT//PnPEI9Gqa2tIx6rgrgkl8+jqDoer5eRkSF+8NB3+fkjP0PXNG686Rauu+km1/yTZbT8KDe2dtBbXMWpEZVgOEE2c4bmyDBbq0xyyQWQ8AAKqpQUcnmswUGq6mvRVQOl6JAVKtg2gaJb42D6Wy8RKJpCsKWRcN0qhKGjxmPUrliOlR3G45RA9Ux++7mdz/DUr57GUPz4/TGQgnwmi+M4GFoASZpiOUOpXCL7nhyhiEtJnc/nMTxeqmtqGR4bJpdNE/Q6WKZVgWjMbe4ViiWqoyE8hk7AcAs8T2i1E5qulA5HDx/mzJkusrk8mqZhGAa27ZBMjSOEIDOWpra2jsWLF884VKRtUcwlGRjOki1L0L0sWrrQzX91yqCc38917pQfJLlsjp8/8iinu7uRZhnbtEinUi63eD5Pf18vT//yV/zJ//wf0xo6mPkMz+7uoJgt4wlrFBGUhJdMXuC3BEKnAhwU1CSq+av7/ieLm6v5/iPP0NU3QEn66MyqCMek53uPsHPXy3zuY++noWYqd82lo3Uhre7ESqQUoOqVcl2aG1oVgnfcdRc3XH89L764i8cee5yx8TE2rL8In8+HlPDKq3vIZHLY0zTC+sZGFEXDscuMDQ8BbhktKV2uLdtmFkZH0iRSSCdH90gKXVgkB5J0ZcdRFA/+gYNULV4N1CKQaFUh7HsvRnz7FayWNFKcQah1aLYOiiQUq8IplSb1GiEE3kiEbe9/P45tUSwVKBTS+AM6HW2HWbl6FcsWbEOoOopqTM5vOBTig/e9n0u2beG/vvktcgdfp8owaC+AKb089PBjKKrGhk3rWbbENUu3rVlIsbeJZw/2oxtekDZSSpYtaeLalYtQCmmYdnBJCcVCEdu28Xp8k34y27IQQmCZNoriVAjsKnMsTRo9ebIN9ZSGS+QKSfyBGIWSJKSbOJkhkCugUjvzmsu3YWVGOdLexUjq53z9P75CNBLDlA5NqzbwoQ+8j8VLlxGrlNXafsUO1q3fwO6XX+LUqXYOvP46qVQKRVEpFkuMjY2BtGlrO8xV112HrutowsvVqxXqipKfjnjRMwZ5y8HSYlyxQnK12EW8aQdutUpczcYqIsMqZk5Ht0pgCgLhCKWaKKauoAkHjZn7xMIkJ/zEw9Vgu9Tgpq1SSmcIyJnZEu5ehrJVQC0blEomVdUBHNshn82jKALLKuI41mQbBwezME5x6BiJyAIMj4eBwXG8QlKwTJLpMXz+BmaoUYBlO5Qd8BkqHkNn0YIG2nuGMC2BPkW0gqZp/M3H/4b//dGPUSqbAPi8PlRFw7FdU9Lj8ZLP5xgfG2dBBfANuO90Vy/9RYXRoQGqGpo5dHqY4ZJBk2MCb/DgmphkFIVsLovPMCiZZQrlEooQ2JaFx+tj10u7ZxxcllAZPXOSVw90EFPi2IaKJnV6288wlpXUznLACyHQNJU777iDa668iqNtbfzw4Z/TfuoMUkpUOcabtl9PIhadimiISjK1ZaGoGk6lWrZba9bFzYhpWFdN06iurua2297C1VdfRU9PL8eOtdHT00skEuHtb7+L1tZlNDc3T2p0qqqSTCbxegz35REC6TjYjqtRDg0OkUmniVdXTXQKhGBJQ4KTGYVETT0lvCypj9I9niZglJDSdlNVcKvyaC1xzD+9HOVHR1A7I2BZyIgPNBtf2TiLf3yKSFFzqylpOkIRLF+7kVA0Pi3iI2a0Abj44s2sXbuGk+0nkY7kZEcnJ092sHTxUpa3LmZhc52rJSHRFEmt34t0bBzbpFwuo+sG2bxFIualmBrEXzMFdhU4aLpGMjVOW9tRorEohWIOs2wRi0SprW/A0ANTPigEmfFxHFSOnRlgrOhGVhUB8aoE7Z3HWLokRFRMmTIrljSz4r4/YzyTo5DPcd0V25EIHMchHI3R0lQ/w5WgaRrxeJybb7kVx3EYHhoik80yOjKCaZl4PF7CoRB19fWTpeXd/WJT8tZw5KSNUJ1K1BC8hh+rkEPI8uQMSyFQogH0JXWUTvSTRxAMhXD8fsTSZjyrV2DNIgQUCJxckUK+iF0JIkrpID1eivkstpw4Fl0nQevy5WzcvAnLcRBSYpo2CNtNYDd8bvBGgabmJnz+qdzD6qCGWRznQNcoVbEoff19EA5z41YvAY+baTnbyyelpL2zl1g0xKrmKkzL5LVDHaxdUs9s9NSyZUvZsmULJ0+edKvCy0p0tVIEWUqJFIJ8IQ84FT+kQqksMU3wxltQyl5EoI7hsU4UXZ87TWQOOe/B5fP7WbxoEQP9blKkW8VaIDQVTdXRNZWb3nTzzIWRruN8QLUZ8xcZNIZQFciWbUZz41jSxpgDrSxwk1svu/QStl92KbNvg5nskxKzkKNQLBCMxJASLKdS/lfalHNp9EjNDL/NRPtwOMyqVWFWzRNYmBBVUfGHwpOanVuBROBRNTRdIxaJks/niFM1MQB0X4SRVJKebIlgOkdnVy/VAR/7D7exeJFOvDU26bMCUKVAxAPkUxJH0ymHPOCY6IUC+MGrzg0LUBSlktPlXoOGcf6MetdM9rFu7TqklKybRSE9fX59oTiZEpSkRGbzmJaJrjsY4+N0jRRoDDoTrulJd7vj2Ox86im+8MCDhMMRisUsZctk48aN/J8vPFDRUKbGo6oKQveRd1Qcx8Tn81Eum3T3dBMpugVD5+pfPByEcJDGulouVBRFobaujlpg6dKluF05OxInAMuCgvRim2VUp4Rll7HMAk+92sXKTdUkVE+lFKzrcjeERnffMJZZImwayFyRcjbF+OgoVYpAmwMJo+keVMNb0d7dQ0gzdKqWXoSiGZNzVCqVCQaDPPhvD6DqCtKROI4zOY2qqkxG+RxnwpRzSWcURWNwtJO2UwOojknRshmI+Lls8+X4vcEZsIaJOdA1jYChUiyV3H8LSSmXw9Dmfhf/5mN/XalS7xJFun93MM0yhUKxkphegxCuBm5Z8Osnn2LglVewnTpMR6UkPfj8MXoGh6hbI+YBDc2av3N9OHFQffpzn5kTbDph82raFMshE1Pur6Y6UYdpSVDd9AiP348RrELVz13scpLfakbk6Oxnj+z5Nae6B+nUFpBOJykWSwSjcRJWP4v1DOvf+TdzYk6mh2Zn//90qWuo56eP/2KOKN6UaNNJ9SR4vUGSBw9wePdxzgT9DA/2MTwwwPCpI0RXXocRrFTXnuYMthR4cptGISdIjvZheSMsCPpYr9qEaiZAf38oTNMcGDEJoWgd61uX8eDjexnK5MimkghFo765hRvWL6d1scJkro5wWW/Lo2cYPraHfD5LLp91D1fdQMklyfccIR7dBkJFVmpza4pKb+8QL7/8FN5wLT98+Mfki0W6O9r4+DtuoipR8wcc9/xSsiWl409To0bpH+whMz6CYfhobvGwePkt6B63RJkiAeHgWAqav5Gir4/O3i4aaxL4/WECm68CEcBSJq6XKRGGl6plF+FYJmXbcrMRdC96zUTKiztuw9BZt2Etqu5SCQlFTEbZYSZ4yHWwu3/XpMtcevut1/Dsvn9n+dr1jIyMMJrJULZt0I05k9mllIQMC49PR0goF0rEtSKFbBZPdA66cuHWNz0XzfT0ngpho2iC04UIK1deRF/7PpRokGTa5vkDHWy69sKgpefVyyY6pmnaWb90Xas422cG0wWShtbVfOVfv0R/32kMxeLQgb1ctm0Lm6+4slK7ev7nTQ71HLBjiYMVbuTZlw7x9NMvcPBwG+2ne3n8V8/SN5gj3rp+noZyxq0/13Oni6qqqJpaGa9+1q8p080VI1bDsYEMqWSK40cO0xSP8vq+/fh0A8sTBmUmYFcIgaEorGipJV/OsWnjcrasbKQnleFEAXIXUBj0QmTC5zTxa9onc34mNR81S9fSUB0nEIqypnUV0aoqPMEgxVAjgeY56HkUjWwyjaqreLwGwVCAUrlMPOTD451KHJ54uhEI07ziIgIBP32Dvfzo+9/m+ed3UrQdhmwfes3is58x2fc3DqufvXaV2UAPVWMFIqxe1cjyRQ1ctmUj69atRq2KYepe3IJcLvJc4oCqUvPmWwm894/55L7nea5c4hlsgotb0YRAnQebqPsDPPGrx3np5RcZHOrnu9//Dnqgasb75LpRtGn9AynF1DauYPTk5AU0dcVqwTgLl29l7cYNeD0qb7n1TTimhalUo4i5ed7y+RyZoX7Ucg4E1CTCLGmM0dvVxXkSZc4325NnyU233MrHPvtpFobKOMUi21c2E1YKbL5oHVgXljFywbmK0rawhMLRfXuQQnDRxo2ujSwrEzZrFiZegpPt7YiKP6y6uppEdTVzqkHT2jm2hWVZGJ5zgEOlxHIchoZGcByHvp4e+vr6SNTUsnTpImpqEpOdmmli2pRNkxPtpzBNi4vWrplzA08fw8Rn5wNDTnJqp9KMpTJoiqC6Kk7/0AgeQ6Oupvos0N9Eu0KpxNDIOL1nTtHYUI83FEc3NCJ+H5qqvmHF47fNeZv4/unubkzLoioaZTyVAkWhLpEgOM2XMqXBSrq6exkdGeHo0SMkahI0NjTh86osWtLKBBHk9L6UCnl6BwYYHxvn0P7XaVrYwsJFS6ipThCOhGcguwFsx0FRJhKFz33v/q6gXYCxdHYyAqoIhXKFXrgqGmYmuNn110mgbJXpPNVB0BcARdDYOIW/m6svjuMw0N/H7pdfonnhQqriMRYuWjrr8p55yThICrkCJw8fIhSL0rx0ObpwkGK+fAYYGhmhWCgQj8foHxikOh53WRzm7JecR2GYqDz0xjVgWRlXcmyc0bEUVVVxhoZHWLJkIZo6I8tg3odd8MHl2A6v7t7FeNsL9A+O8NY//Rjhqup5D67faUBSki9btB96hdRwH2u3XEU0Xn3eDejYFv/w1x/mueee4U/e817e84EPz9vGkZLX97zKg//8GXRd5+Of+jwLFy6ak0pDSkl/fy+JRA1DQ0PU1NTOuP3mOoDOLdM1rZntbMdh+HQHh3Y+xMmeDO/8yN8TikTOAobOfpbjuP4Ez7kO+XP07bcfw/w/Q0qJVTb53kPf40c//BYNtVV8+nMPUltfB9I1J2cPx5GScqnIj772L/zX17/Gjmtv5O8++wDqnAe8g2UWSY4NEI4mUFRPZT1ExVz9/5dIKfnZj7/Pf/zrl1jRuoxP3P8A8er5KzTJymX97BM/pth3hP3Hevmrz/4bXo+GRP29FpL9/4jMO6DzwCGmWjtS0vb6Pq5o8pIbLLDr+Re44bbbJidr4gm/bQ7gdLGR9GSLmENt6MOdvP5SmStvuWf+Q77iexLC4e8/dA9/+87tFPwLmFFncY7+pQfP8JG3X002X2Dvnj0sXLhoxliny8jIMKVSmdraOoaHh1y66mhs3vE9unMnhw4dRhcCXXMdp7Z06WpztsUtV13D+hXLz3qSoihkRvpYHpB0ZMdJpVMEwxNYtNkmtBu5Obj/dZAOI8MjJGprWLJ0GcFQ+CwN8ndlGLhgmTbdmqpx7PA+3vWmi7FyGfLZFI6sr/iD5jDRAek43HLNZu68ooVjp8cwzSKqGpz18yW2bZEaGSYSS6BqfnK5JHYxgzcUxeuL/F60gf9ekVy6qoWrP/sXpLNpdMW1xWb4nmZpXGa5REssSLVWxcHdr3Ds8EHWb9p0zkN7rrSu+b43UYnp95lL/IeQ8wBQ3Y0mkTiWycatm/Gkz7BsUz1ZbwghLcQEIGua7HrpRfK5HKtWraGxsfGCJkFKN3l3UcTPkdgiwloJbeXVOFJO5tnNzI1z/9N2HEoj4+Q7+9AKA4jGOjc5T5m+8JNlLJCOQ/9ohiqvwC6beI0SwrGxhEBBML2ojpQSw6PT3nGMoaE+lixdjmmalSjj3GbKrj2v8cgTj6EoKpqmo6gqTmX+pO2wYc2aysE1fY7BzpzCW+4kY5VZ3aLC+AlkQx1C0ZhuErkvsM0Pvv89CrkctdUJJJL2Eyd4ZudOrrvxJtasWTvZf3AhI6ZpnpVf+PuSidfCAcpmDo0yNR6HV473kf3pL7nrHXWEmiMVLqipRhNrYnh9lDxxho69TlVNE1ol3WX6CyyBnlMH6Boo4Tefo76hmWjzRtL5URwnhdd3dpHd6XMwIRdq7l+IzBfkOd/zJttISXXLGoZ3n0TIEvrgKE6sBqVS62BisiQ2oIIsUcj0ULAzFHQTOxilqirCRJ3suUY24e7oONFOvLqaWDw6o1/TteVTnZ18+1vfYv2G9dx6y62TecjzKyPzmZWz52BGk+l/uB9O/PyZv51TznNwOThCBbuIyCVZ0ryUYrvD0iXNKCGFwc4j1C1ahVCmkONCuCyj3/7ON/F4PHzwvg+xfv2Gc3ZmYnLHs0VODBZYsHgrHnsJpmIylCliCAgHPBjTaHYlUBoZZd//+hj51BhGTZSMWcDT8wuqmpaw6KPvI7plw+SXbQGYFv37XmdbJEGgMUG9o5PpMxl4eS/xi1ajhoJn9TMcrsKyOhhLjlOdTKLrHizLmjeK0lLfQF00zmg24x5YjoWUEs2RxMJRN51n+thxsNJdiJ6f0BgpYLbWsqCo4Cs8jX20DbH03QjvzNytfLHAkaOHuHjTxQTDQQSCbC5HWZo4s6hXpJTs3LmTr371qwghuPvuu7n99tsra/X7O8QcoJjK8tN//hY9+0/jufhORgcGGN97lKcHHuHNn30nindmNEzakp0DXXyh7TWu1RXuXb6CtpLJO3/zQ25pXsGfL9+AX9PcCB7Q0LySsZ7HSVsBBlIWqb0PMTjcz7pL7pyzT1K6RHrKefjDpkv7q4c49eoBqiJhVFXDLJUwiyX8sTCeaIhULkdt6yJaVi9DVVUcx+GpX/2cM+1H2XzpldTWNbBv717a2tq4553vobGpac7n2MUSxcdeQu3sw5vKoOJg2U+jRPaj3rwFbd0yXGgPLgW1bZEePELnmX10HDvFxRct56pLV9G59yd4tZuJN65BETNf52Qyyac/eT/SUdj/6j5URUXxaCia4BOf+nu2bN08tX5S8h9f+U++/tWvUSoVuOnmm/nIX/wFWxznhvEAACAASURBVLdunfeSHhtN8uvHf4WieznV0UE+nUEIBV/AR6K2huaFLTQ21LFyzXIQAic1ijncR1bz4JcWarlM2XYY6zpNcPk64i1LmDPKPYec++BSKreDaTNy7FXCDSvx+8NIzSTTfQpfpNolexMOU1qB4PLLL+eXv3yCsfFR7v/s/Xz6U59h4cJFlEol/H7/WS+9BLLlMv1Ji460gSkcVtZVo6aGqfIbjBctpO0g1QlfhntK9z/6a8Zf2Yt9xza8Oy4hWBRY+19j6KFnKH4iwyWPf9fVvITrWLSKRXK9gwQ0L8VhBVEWKB6dTP8AoZVL0ESAmX4oQaK6mkAggmmVyeVzLFvayNDQII2NTXM67etrEkjLZnlTC6PpNMVyiUQ8jldVGc5myTsWswcvHLDHi6gBGzM3ijdg4+SzmIUhys6z+NbegkC4NzEgHZuiZXKmt4tQYCVjY2OYlomqwSu7X2Hd2oumjUGyY8cOnnn2OR5//Bd85v7PsHzFClaumIoKTmg2M5gbKkMq5fOk0ikcRxIIBAgEgu76CSZvetc0l3QePsnxZ/bgL0b46VdeI6AvoCFYQ3d7O6m+EaoW188YuiktPvTML2g3VF7UNHaaAXrTRU6Oj7O/r49GReePWi8Cxe2b7gkQa9mM1vMq3kgtiVAdDauCFLN9bnRPuj40t9qUpFQq8cjPfl6JaEq2bLmYbDaD4zg0NTW5ScnTtXhH0nXwOLu/8yhGNguOROLgSInlWOiqhgwEWXzd5TR98s9RFAXHsdn92l6SI90UbAfNsilKhwN79rFs2TJuu+Ous/aIBJyeIbQ9xyktqaUrXKJZr8ET9GA/uw952IO6soUJqLoD2MKhr6eNUj5NYmEt7b1J+k+coaa6lt5DzxEINqBGp8gCpJQ888yz/OT7DxNOuMVoQ8EIpWQWSjb7njvEmjWrCAQDlbcWenr7qKmt5VTHSR595BHa2o7zkb/4MG996+2EwyGm00kLIWg7fIJ///JDqIqCLUrkB5JoQsVTFUAqKkiNhYuaePArnyMcCVPqOsn4Y9+iK5Xi2YKHUjJFuZhiQbHI0gWNXP3Zr6MEZ9aWmE/OA5pQKuBCCy2fZmx8jOqGFkZeOUB1s40nGHIPklmuC5/Px8aNG9mwYQNP/vpJfvXkE8Ri1Rw/cZyVy5fzpje/hVAwNDkBAnDQyZkSxRrDJE7ZUQnFW+gZSxP0+dD02aS/El8QxIII/rCGUhrE3tlLttzDmGoTxHSrald8Ky4EQ5IdGSVcV4/PH6KolFAdE4FAEypzaYWlUokVy1fym6efJJ/LEwwE0TSNoaFBampqz9JaRtMZesZGGMnn0XS3zPmZoSHyxTymWZ7Ew02MRQqBogUp9GfQmwSBhjDmYAbbvxarZj0kh1CmHRJIl/H1xLETZJJJ6qtqMAyD/QcPkC0U2Lf3l7znPfe6ar47w3g8HtasWcPatWv5yle+wuc+90+0tDTj97mpOW95y1tYvXr1pElgWybPPvU4Z7oGGervoe3oESxLsnDhQpasWI7X8OLzCG65/S4Mr8/VCqSkWC4zlk1S462h2dNEOBjHNkuUKDLUM0jVorppdoNEAzZJk86ubsoLl/CbVA7VtlHSKd4UhZZcL1K5aHJVHCAQjuBftImyVaRYyuNVdcTIAXJqGU94HXo4NmmCejwe7nz7HXz729+lp6ub13bvpv3EMdLZDNdcex33ffBDGJ4pmiFHOpRzBVQh3DlXJKZpoWsaaAam4XGBlJaDUuGq1zSd62+4hfvv/zSFskJVVZjezk4Mn5fDB/ex5ZLLaGhoqlwGU2BdApLyShDhGMtKURSvhj08gNOgUvCOoJYr+0c4CFTGBk/hUQukRjKYaoA1LesopfJUr76M/tPPE+k6yaLKweUCPS2+/uA3MO0ymXQSj+6lXDRRFYnX4+WxR35C7aIYt995G0K47+H/+0+f5B8//tcIodDf30M+n+WxJx7n4MED/NE73kwhn2fz1h0EAu676/F58Ho9ldxUCwUFRahuhSJNR0gVXdfxeF2LzLdmGzJczcjP/ou72vdgllMuANcLvcPdjPzkP4nf/A7USBXCOLdb45x8XFJanxQoyGIGx8rRfyqPowkiDQlSg90EYgmUYBwh1LMcoytWrCAQCDA0NELn6U76+noI+oOk0knS6TStra0znIDZYhnbdhEvjscgWZaMFhwG0zbxgIJHkZWqu+7ta5l5DKUPsygYeOokSlMN+QCceellbvzC/8OyW9egNa6u/Hz3mHBKBfJ9fWheH/lUBsNxqwoXshlii5pRZ93A7sZ07fzR0VE8HoNDB/fT2Xma8fExampqzuLk+tlTv+bQsWNIQ8NRwVYk0lBRPAZWqcTV27axptX1cbk3I1hDnYw88TOMqIOKBjrkMjZG1XK8yy5BVbQZEaN//fK/0tvXxerVq2luaMZxbErlMt/53vcR5RJXbd1Moql52igE2WyOrVu2IIHdL++mvf0kzz77DK+8uhspJVfuuLKiCUuscp5jr/ySloUtLFnSwvJlzbzlzTewbOlCli1uJBHzcui1p1mxdqObzC4kwgGrXCC9r52EJ0FAj+Lx6MTq4iy9UmPJ9g0Y/vAsDafI5twZnjWhz+NyqFlIpFXmfWEPN7UuR482Tc6VwEHXDKTioZweJjkyRD7ZQW9aJTnqMNzxOlULlqFqbmXyCc2jr7ePizb8X+reOz6O67r7/t6Z2V6BRe/svYuSWCRREmVVq7j3Ihcl9pviFDtPulOcYsc1fh7bim3JTlxlW7QkqheSoiT2XkAQBIjeF9t3p933j9ldACRYlDh53vd8PvgA2J07c+uZU39nFX/7d39H+9mzzJ0/Hykhk8nOgFuxLYuTO/cxcvocil5wiu7aFoqikfdqeBvqqVnYhjvgZ8XWDShF131dXS25nI4tof3wHqxclkjAjZVPY1sWmckxgtFKvL5psVO5IRKJcUL5CLQPYA3FUYeGcW1cTt49gad1Oarb54QcWTbDZw9TGE/Q3tPBvPltdPScpC2kUeuyOHu+nRWb3opvGs7drx//NT/4wQ8p5AvYpo1uGLgUD6lkgnhqnPR4ksraGlauWU4g4AcBwVCUWMDD4uUrOX2mE7fbzaKFC3jg3reQT4zyy8efJF/Is3zFaoSAnu4+Xnh2B1IKVFWST+dQpMAddCM0NxKIVVdw/9vvLq+hKxKjet1mtMpa8ueO4XKB2+ui0qNgnD9L/sgOjO7T+FZuQGiuz1+KN11B4iqqArbFi0+9QX8+wFK34PjrJ7llUzP1Pg9SFtNspp13IUpVjd3cdNONLFq0gEQyQSgU5tixo5zvOc/zzz/H7bffWZa4dN3GLWxQShG9KpoqSOmSTF4Q9Gp4SrEkgGoUkMkJQuEGtIEXSf37ywjLoGnCxpXJoTYEKTEsgcCWYGSTRF0uVFMBKbAsnUw8TsFMYJPnQomrlDkQCARYtHAxw8MjLFtWydDwEIVCnsnJBBUVlTPaGIYO0gbLcja9pmHmckjLeVOLC4L4BBLFyILXhRj3kMuMUbCT2HXVVDSvcJKlp1GhkGepNcKLne1Ett6OP+DDll4mxiaQuRzvaKtE8QWnGbadA7xxw/XYts39993L+e4u0pkMTS3NpFIpDh46xFe+8hU+/elPEwgE0DwBbnngEwz2nqWr/SgT45PUVweZHJ1A6mH8FfV84FN/RThagiCRIBSqa8LUVdSRn1BRNAUbydjwCA2+agfZcoZoLsgkxohEQkRCEkXmkZYNto3X7UEVAVyaD2bE0am43QqapoAeQ4+fQFgWPiVCJjmBYuQYPbuf+iUbUYt2V0VRuPueuygUCnz2s59lx44dAIyOjvHqq69y/nw3t27dyrp1a5G2pJDLY7k0ZDSKIkHYjmkcaeOvjdLf3sWC6ypmvKc1zcXHP/4xdL3Az38+nxOH95OJDzM8NsnPf7GN6vpG/mzBciIVU2uei48SrG3BPNgNlgRpYQoVV8GCwHSjv0RgYBk6A0PnSGZByxZorqwgbSYxC4J73v+X+EJVM/ZJMBRCYqO4VTwBH7YlMaWON+gnoIbIxVNU18SKYTTFUyUE1992P2t1g1h1LdufeoZMJsV3vv0ofX191NVVMadtKig4l8tjGAaKENiKk7ZkSwfyBqEj5CxpfQJUt4fwhjvwzl/J4C++jzY+BIrEsHTwBRGRSoR2+eyaK5QnczafKVUafYInHnuS0VNjNIlKjrpOsHDDehRxKUBiJ4+qvr6euro6wPFuNTU2I6VNTU1N2egncUxRuVyS3GSGSCxK0OtDmDrCSKAIFaG4AGVKxbIhH0+T3H8crxSI8QyWUcBGMLT9JSK/81a0omFTSAUhTJLJOFIqpPMZhFK8l0shVOHDEwnMGotdOjRtbW20trYWF8zBfopELkiBEPDRd7ybTWvWU8jlmZycRDd0wqEwE5NxLGlz0/UbnOeKUhMTS89ijFfQPaCTajRwy1GihVFiWBeFWHo8Xlbd937aXtiJS3M5VaMVQSGf48FahaWNtQSiFRdpvaWo5aamJr74xS9iWRYjo6PE43EM02BOaxuBgGPvUBSVQKSK+ZEq5i2bKnQxK8pm8beNJDOaxsgpjlFc2tg2GCJHuMZfdDDMdC8J00ZR/eS1HHYu7TAuKdEtm7w/QD6XJiCdMm1ldicEqqoRrZ1DtHZOWS3KZbOomorP559hTC711ev18vGPf4yPf9wBA3AQF0T5WoFAdWnUX7uU9tEBLNtCt5wK0EIR+D1eTI+LWONqWtatnDEHpb89Hi/vf/8HMd/9Xrq6upmYmCAcDjF37twy0m5p1RXNjeZTsfKmA0ZrOwgL+p5j+B9YhuYpSv+2RCoeFiy5nog5gRbJcOpYF6sWziExMUJwrgtPsAYhrBn92Xrbrdx0yxZeeWUHhUIB27LJ2zq2rRMMBGma08z9b3srofC0sJNiW4/HzT333MPdd99NfGKC/t4e0ukMsaoYCxZNecSXLFvI7//xp0inMxTMHFK3UYWgYJlkc3lcmpu585pn9RQKRcFd00jrb/85AGY+i57L4fb50Lx+rkRXkLgcvTydsdh9coSqihbi8TE87nHm1W5EBGMIrhzVXVpYTdOoqZm92o3LyvDi0z/mxd2v09jQQDQSIZnMsHpOJb5btuCrvpYZPmKhYPvCdAxNEBAKFgbSrYBUmUiN42u79gLtVaBpCgeGe+k808u6JUtIpTLs6TzFR997CzMtT5cfR6k69SxXsLitjcVtbZebDaY5g5EoGLk0sUA32eoo0dYmUicHMbrikM9i+71ciDnfuuoaHvzCNwmFA1RWViAQeHwBhtasYvl1G2huab7sOMCRRErVrS9Hsx3QmaOZcmVLO8O+wnFSkwY3bLqZw8fPEg6MEaxe6uC1X0CBihrOdRwh3Jeh3tTJWTqmtAhZFrF6m1ROJ3CFcZRSYkLhKxt1p/e/5CCaMT4EN969lRvv3lpGsHD8QLMVqJ29X0IIXC4XCxbMv2Q/JOAKV/HqK2/QpxSYSCTRsym8msK11QpLPeFiKT9AWIDCyPAIx48d4eDECCvmLiPvVtHcVUhDxxZcVAdACMHD3/02L734Mslkknw+j14ooKgK9fUNXL/heiorK7jUPik5aypjMSpjsYu+A0ltXQ0PvPOuWdvPnKtLfD5tTjVfAM1XTG+7ihiLKzAuFUWCLxSk9eaNFBoGee3Z5/E0zqU3ryPwoAr1Csd99o5eSF59jHCyh1Q+w4HDh0jHJ2ibN5+PX1+HF8sx+E1rb7lcnJm06bz9Wtp75jLa008wWsGWlQ3cv7aOgiXwlbeXMxGhcIjBQpLvv76Lf9vxCs1NzUxk8sR27ufDS9fhuzqc/kuOpSRuX0Xr8l9SApaCoQqYGMSbm8Rn6GSsE+T7TxKYu6ko0k47dIrG5hs3z+hLU0sr3HhT+fYlz5rzjFkO3iX6+WaDVYtZewhFEqiJsWrrMr7+7z/lprUPcOdd9/Ov3/pnPv/j7Xw6vJI5bXNmbBRbKFTYBpvyQbw9CfrPHKdmziIq/SqrFtu4PT6kIi/JJK40ljfb5lJM+jd1/xI5vkoXveNjdIdd7O3sJplJs2LRPGqDJksC0yUODYFNRV0rQ6ISxdJpaVvJwb27yKsK9950J6oQzKb3eL1e7rzrjsv38z89jt9sAZeZMsaVb3zFVGwBBEIhHnjn29j+/D4yuRSaJ0DdkgVTydIXPmdacOFUXy7XGYG/dh6Lb7yP64wX8fv8GPkCBcvi1RGNtpsXX/QMlzuAa/46FnkHUIK93H7bZjp7hvGEbbyrt+INz9T5JQquiibe8aGPc2pgEl8kRltzI+1numlduRwtFLtMHZ7/PhKKgnflzXRlDTrbTxGOxeg7d5p73vMgnqpWpHpBAvsF83hhPiUXXHshI3LS64rhDmV19VJrI5G2jVMZu4SAOTMq39kBNhLwxRpZc9Od3NgzTlNdPddtuIbuvrfT1d1FMj15cf9UN8ma5SxePk60NkhkwxIShkQVFkbTHCJzFlyRab1Zutoo8v/cfade4aW/ita5mTZgBGqklvolayj0jvD+d9xFRTRGe9dZKhor8TUtnbqvACElnqAHEapEzdo0L7+e517dw8rlK6lpXDz1jFnoTY9ROthj5f4rjnR1uWeUTQhSIktqYfnSi1ewvAZl5UNcOEVXpKvPVZQW6VSGb3z1K+j5DL/zh39CRaUTDe3AJk/R2PAAHQeeZdnaW9ACYbyByFWV1f6foNJ4+7o7eew/HsE0JL/1h58jGA5ddnH+u/sDMxnN1W440zTJ5XIEAoGrmuN8PsdXv/TPDA708Lk//2vqG5ouyRxsKcnn8zz6ve/Re/4sH//Eb9PSNncqX/PiHYkEsrk0z/7iMU6cPsG9D7yLldesn3aQ/2fnd6prknw+x/nzPeUYLvjNMS8pJdK2eH77kxw5uJdFq9Zy770PUIZInuUxUtoM9Pbzg+9/k1hVPR988JOOLUzwf2UvOn2SFHSDx372Yw7u2c17PvQg69df5/RmFlsVOMZ427L4+je+TkdHO+9997vYfOMWlJI6fkGb0sv2lZdf4ic/+REbN27iQx/+6EUvxVkfVqSr5ibSFhzc8wZefYLF1Qpnj+7BKtsDZzI/T04iTsYZePwZxo8fJ97Th6nrv9GcOdu2yeWy5HJZrKmOXB1JCIgC79myDHv4DIO95y+pVpcm2TBMDuzfx/btT5NOZ35jYyktlmHoHN2/h53PPc3Tv/wZifjEZZ9R6ldPTzepVBLDMK7qeenJcZq8OW5dVkVh9PyFS3fhU+juOMng4ZdZXqHz5C9+hKopRWn64oalT0cGB0h3H2HLgjDbfvQ9LCkR0r7Cs37zVF47XedrX/sqDz74MW699RY+9rEH+ZM/+ROOHzt6xaLGb4bik3HOHd/LO29awfkjO4lPjBaLws4+cCnhmV/+hEZXklzvMbo7250rpbzsXF0MTfSbpVQqzsu/+j7rmtw8/6sfYdvWVL9mIUUIxsbG6O08ybyYyrFdz5QRUS8sOguAEBiGwblDr7B5rpc9L29zygy+CbrqgrBC2qTHR9iybh7VlT6273mDNRtvAdVVliudcUn0wXGMgTxySQ0TPz/G6+d+zroH76bt7ttQlP/aW660YGNjI3R3nUMoCtXVNQSDQSKRaBmC95I2nGIaTldXD97hbuY1VaBnUpRyGmfj8RPjo/zgPx7nh//+U2wpuX79c3zqoQ+xYtWqcsjBhX2UgCjC1woATTjxbrOQbdvs2/ESbQsXs3jFagy9wLPbfsnNd95NRazqovuXKJ1OMzo6xtq16xgbG6Ouru4yc+uoetGKCu5/yxaM9BD5UKj8eWnOSlqkBCwEmazJmpUruHHtAv59xwkKuoHL5XYSEmZ5iiVNNKESUKE56iLi9yBkMdtuWoP/LpXtQrJtm2/+61d58fln2XPgGAAvvvgykcg+Bvu6+Oa3vkvwghJzV0ty+h8S8rkck+OD1IeWM9jTyelTJ7huc82U9DQ9T084ffN53GxZfw37D7WjFwplc0XZe15kUr093fQN9LJi2Qr2vPEioXCIVas34/VeHHv4X6VoOMyf/eGn8aaH8Cy4ASntcgB3uf+lPSNB2jYnjx2i1pXjo/ds4X//+FnyholXFagoFwNES4nmUnnHA/ditO9k8/VryiLUpcweF9JVFYS1bZt0OsEvf/gIbivJ6weO8IsX9pPK6iAEzU3N5V0sgV9+59vs7tpFTVWMnftPMZBP49FtYsvm4Q+HLuiY83YpRZULIdD1ArlcFtu2UVUVIYpa9jTuPdDfS19/H1VV1YRCIX71q8cYHxtj7tz5l81uz+sGh559gqce+R4VdY3Mn9vAL3/6ONV1rdQ0NF50GnXd4OGHv8svf/k0+YLEtmySySQ93WdYsGgpscrKac9ymIDZ3Y/1+h6SX/4mQ9/+Lvbrb6ApHpSqSvB5LhKdLdNk366XWLrmGvzBIC63m0R8HJfLTTQWYzYWIaVkfHyMWKyKUCjE+Pg4Lk3DdUHB2RLZts2+/Xt45oXnON/ZzoLmWp7edYC8rVIRrcBdbGdBOdwqPTHJyNHTeMws1Q1VnN97nmi0Fn8kiOr1zAiKLR+wfad56VtP4s7k8Ls0zu4fIuCLEZvXPANrybIs2ttPMzkZJxqNFvMJL79hpZRkMlkyqSTeC7y6lmWSmEwWVa2pGX7llVf40pf+hcb6GiYTaRRVw+fzsXDBXJLJBIFguJyUDsUyHgKnojQ4+tHl1PfiZYlsllf2H2HHgd2EFdh+agBfXSvzWtsIeDzTQkpKEwyD7Z2cPHgClyY4cOg8sqAQjkYJVFaglNUmyalTh/jc5/6IvXt2sXPH4xw8tIeXX3qVDRtuoqLS2R9XOutXSjYvfZ/L5Tjb2cmZo/uoCqm8crQHdyBCIBDA5Z6ClHb2icREwmSOw8/sZrDnGCsWVLLzmdPcoq9A0VREbeCifQJgmjapbJZUNu8EJvtiqC7ftHED/+mCsEUj+8DgIH/5l3/BkSPH2EaBdFIHTeP/fPtbHD95nLVr1hTxz511bNlyA8+f2s0rJ/ZxPJfFk7TwDsG6/hFkQ92MiZPFdJEfP/pDOto7EJaOkcuQTqfx+4JEo1EyUhKtqOR3//gPy6ij9fUNHDx8iG/+729w333309Y2j+5znQwNDtDU3HKpEeFRbZL9nWBJdrx+mHC0hu7D+zi671WWrb+G6YdHSskrO3Zz9NhpFi1opON8Ao9bo6rCxaHj5xkeGWXhgnnTrgfLKJD76a8wX9uLFBDRVVxGL5kvfQWx9Wai/+v3YHoyrARVU4lEKxjs7cE0LfyBQLEkFFyKaRmGgd/vJxx2KtrU19czODhAIBi8+HoknZ1n+cI/fYFMJs3yRUupbl7Er17YRfap5/nIBz/Cu9/5PieuqbixDNPi9LZddO06SltlhKO9o7Tk69jz8OPc+rvvoW7NkvKgy6GShs1L//JzRk6fYwg4/lI/0irw67/6DiLiYdVN180YQ3d3N2fOnGbp0mXceOMWPMUyb5e29Ule37WLx37yE0KhEJYNbrcb27YwDAOjoPPJ3/kUK1evLnumOjo6GBwcZM2KRWzesI58oYDX68Hl0jh05CTHjx+fursteWPb47z+k5/hRmHVfW9l3Z23M9Y/iOr10DR/3tSKCCeoWUFiS5ufvfw8v9i5HxlZxDfOWBhNy9l+5Dj+aIyP3vGWItyyY7yWwGBXDz/60r/gVnw80Z4F6WHgxFmeGx7iQ5/7IygzZsHw0HmqqjzkC27WX3cD//CFL9LcXMnu1x7H5b6fSKSScDg2K2N1mH2GY8eOcKb9DKtWrmTO3HmEi5WQprexbZu9e/Zyov0kuXEdd2UN49kET23fTktzM3feeSehUMSJkiquuxxLYf3rG2w644bBRoLHGvlE9B60XecRXf0o9VuQ1cEZzyoUHFDGQLia0LrbyCdHUfQs0jIxbAWPpxR8emlufFWqYjabYffre9BL0CguDU0oZHI5Duw/yOn2dlYsd5BEFWDZqpVIby3tQwUCbkEyoNOyYS45OUZ2ZBRfrBJlGk69EIKhwWF6urpRLR1hFMhlM6R9PgrJBHHdZDIyQT6Xx+V2BhUMRbjpxi1YpkUqnWLFslXEKivZvXsnt99xN5FIdNaFFNJk3dJmTuwP0DWaQBpDxAKSrXfdhsREXFDE4/iJs8QTKVYuriMSCeBxKYxMZMgV7NmnNWdgvLaP7GQCv8ePEAp2FwyPjjN58iTXWpZjtBSlvDVJLpPm9ME3OPDqC6SSKapraslmUixbfQ2NbXMceOwSo8fBTBocHJgRbOlyuQgEgmQzGfyBKbhn4Yir7Nm3l9GxMbxeLwePtxOM1JDOFZBItv361zxw3zvKjEMKx9h69NBxZD6PKeqJp7L4LJNcOk7GMpnugrWkRAXOHz5DT0cXdl7H7/Xj1lRGsxPIQpbJkfjMjadpbN58A5rLTSQcJpNx7IZOuTknyMLlmqn2G4bJ47/axvDwEMPDQ1i2hcfjI5tJ4/F4UVQV88KCw0XVQ1MV/IEAwWAAtaizWqZZfnEKIUARzF++nJ3Jh9ELBrse/Xc6dr3Ga6+/ge3z8Off+SZzly93vKxSokiwhU02X2DHwWPkCyahUMAR0nSd8XgfL+07wLtuvpFw0YRhI1EtQXxiglPnerhlw1rEpJuIx0XWKDCet7GEAXjKE1zQ3RQKFqMjIzz88KOEwj5sq8DPfvo4P/vpr6muruEf/+mb1NTUTRMIJPF4nEceeYTOzk42btjINeuvpf30KVLpNAMDQ9x5152EQlM5w7lcjonJSVR3gIo6L1q4hao6R5L3+TwUdJ1gOcTGQrEgO7gf30oFV1UDlTsXceTABOfNLMOuCdbfsBDVXwB5gTqrqEgLVNVCVRT8oWqkbaG5XOi6Nc10MdsBK+6fS381s1zQlAAAIABJREFUpZmNjIxi2RbZrFP4UVM1TNPEtm0ymTR6oTAtLUMQi8V467330tfTS9fB19kQtRj4+fOc7I8z3PUwKz/8Xtbedh+C6WXVnY6qqoawHAQFVVMdiUTRUDUXXp93hpo1NjbOLbds5cjRQ5w5c5pUKsmCefPZ/epOttyyFb/PfxHz0tNJEhMTeHxBXNYIo919rFpYS8CnUSriMEVOsm004qNgWIzHC/g8CkIaSDPHmfYONm+6fuacnenATmcJve0+zCefAwS2KZkM+KkcTmAfPIFYv6asjkkpeXnbz+g+eQDDKBAIhBgvTJIrGBxOxVmxfhNzl60qhiI4a5KzDPRcluaGRqRhIoSjfUQjIbAMSuXPSpTL5Xhjzx6kLYrVpk2GR4exLJtkKkkoOAVYWFp3FUHYENiqB5d0gjwVTSNqu4io3nI0+5ShXjDRPUhqIuHg65smlrTJ6Hks08Y1iw3c6/UyMT5OS3ML57rOY9mSbDaDy6XidbtoaWmlpqamzFhs26a3t5dcMoFQNEyzQE7NYZkFxz5kmux8ZQdr161FSomu63R2niUSDjIwOMzASJx4fJLamipaWxsRQnL02BFSqSThsIM2Wz1/Hq33voWjz73AudPn6BocIJFI4E0I/uW3PsX7//ovWb5uHZGKSmwhUaRCTi+wtamCJ0eGGMtKPEIlnhxlqdfmgVVNKNOqqgspQAEXJo2hapS8RjpXAI9gtJBykCzsmZGRW7feTiQSZdeuXZw4cYxUopNEYpJEIs/QUII39pzG4/trvvD3X6CyMlZW2z/4wQ9z4sQJLMvilVd24fP6EUKiahqJRIJzXV187nOfLUu4uVyOWGUF6YLBisULCUfChEJBjh87RkPtAibGx4hVlsKMVPLpAZT8KNpd9+A5OkLb2TipjEHn6BjjHptMhcDDNKlOSuyiCm5YEAjEUIt72jAlmVwBv9dX3uuXoysWhBUStj/1FIWCXk5bsKXEq2k89Id/RL7/MNHgxQUd3v72t2FZFg8/rKDvP4RRM8ZjpzsYzuo8861v8bdtS1i4cKGzmKXjIp3yRqVo7FJhSUs60Maqqs4w3s2b59izqqqqSCSSdJ/vRsoOFiyYz/jYMHY0VkYELZFt5jnd0cvxI0eJVEVpbGlmeLyfxOQk7loxw81qWSYetyDgc7P/yBDZgsQyMsyv99NYG+UnP/4JH/nI+2fA9Bg7Xkdbvhxx9pyD+onFOFlab9mMr3cMe2gYVUKpunE+l+P0iWN4/QGMpIFp29i6BbZNIplicjIxY14FguFzHSh/9Gd0ZdOgulBsia2pCJdKpK6N2He+gZhmCdf1Ap2dnQwNDdHU3ITfH0IW4XVcqopZBEcEiS0EQipIaVHp9jIYyCORpHUDv6qQjI9imkZ5Y9kIkAIpJBoq7oALP150JLa08HsCpEUaz/TAsSK5XC5uv/12vF4vO3buJharwrYldXW1WIbJ6Ogo4XC4vO8EAss0QKjY0kZVXUWJiSLvVCjk8thSogiBrus8+8yzhEMBDh1vJ5vJIYGJeJz+wWFamut57dVdjI2NO4xLgEDhHQ/+FqtvuIXDTz3NK49tw+fO4xKCnq5uvvDbn2LLXXfwe1/8EprHC1hUhf3curyeXee6GY7r5DUF1evhljWLuGdpK5Y6Vb1cERKJSTDgxVMTZhJJMpukPhYk4ApTVac5Dq9pc6VpGhs3bmbDhk0MDg7wj//wF4zuf410No9QFAKBICeOneC7//Zv/NEffxaAwcFBkskkpmkAjjSlF3RcLheK6pQ5S6XSDtMqPk3qefLde0kn3dhL56MIyfDIIF6fSpMyQm1sTrlPqgKecDV63RL08bNIdYSgYlNhRwkFWumRw2QNgwrbmBbaIYvrZWPbTsHmkuahqWCpGhIHIutKBvrLMi4FC4RKLBoi7Pei2w73tCyTrGlh6Rn+1x//DjK6wOnaNKlLVVUUReGDH3yQzk3nEMDIyDAjIyNEoxVEow4SY8mWUVEZo6q2DtswwNTxhsN43D68Xi+qhFhVNdKyygUjhRD4/X6klKxYvoqh4WFGRkY5fbqdwaERNly/gYpo1UVjUlWBaZno0kYXClYBgv5q3JqHC/HmnGKzFgPDScbHnEIRtp1nIqnS0BAjb5ZZrsN302nGB/qoWLkEa/tuTKmTKOi8nhrnLapA27KRzLZfE7rjZoTLA1IyGR/n7KljCD3rSBz5NC5VYFkSt8fLE4/9mPq586mvL1YdlhIjnsI7OoyIJylYFi63C01xEFzNnIk1OoI2reaglE6BCVXVyOd1li6cgxQW+VyeQsHA5dLKMC1OXJ6Nqug0zKuk92gPuWwGOzkBwRCtyyqoadKmPMlFA7KwJYoB6zdcx4nXDoMtUWyJJlTSuYxTEmsalfZKRUUFUkrWrlmF1+vFljaZTAZDNaiqis3E+FcEjY1NDPT3oxecpHgpLRThQagq0WCI6urq8pFXVZWVq1Zw5NB+LMtmybJlmKaF1+uhr7eXdDJFXX1DWSUtdgy318eS5StZuHgpN9x3H8N9fTzzzDNExsZYtXYtN915uwM5g40lVBQ9R3dcZ8AM4fLbeFSJamoMZk3GklkqLwi4FJhUhzUWtFRDAULNddREI2SiXm576zq8rikj+IUR/PX1Dfz9F75GR0dHUaLsZHx8jLlz5zJ/vnMObdvmiSeeZE5bG25NcK6rl2w2iwTcLheqKggHA6woQRkVn+HzasyrDWH5okTDEQxTMjI8gd8l6D13joZlm8sOA4mFUF2EW5ZjdA3jfnYQJVIL0iKckjQqHgJuP5aQ04QBp61Tts6FXdS4StqHqipOIRRxZe/iZRmXXXRkPnDXrTyx7Rf0x3NO0cdipdpsYpy8YeGZpbBBaaLD4TCrV626+ObMNMJ+4MGPFMNXppXJKk6qoihoqjprPUYhBG63h5bmFlrefbFR/qJUF3clrx3owvIEkcEAoxMFwi3z8Nc2zUB8BnB7PBTsIGnDS6zOqb8tpcR2afQnXXzgXW9xbExFQ6U1OIC3axDtEw/C64cZz3vpk5K7a5rBlHjvuQX7xHGs1/ehFVN2fL4A199yNy9s/zWKEKgeFUMBy5Sobi9L115HwD9Toq1rbmK8rh6P6sNjFjCFwNZUTGmj+QPU+GZer2kuGuvrGB0ZxeXxYCoe4hPD6LpOJp3m5i03F6VGgZA2dtHe0+3P8fJwB+lUAncqSz4a4LZNC9nk1fBYjqmnFDohBfgiPro6z+LSXOQLOacsl5REo2FstXTl7PukunrqJRO5RN6h2+3mH7/8JTrOdKAbBqZhYNk20rZQXRpNjc3MnzcXUVwTr9fLRz7yIJ87dYaKCjfjY6NFT7ULr9eHDXz5y1+moaFh1j5pLhdNixfTtGgx627dOnWYyk5kHVO4MQtpntlzANPyoQqwhRtVWpzoG0a9ddPUiIt73UaQLui81j1MX3+cXK5ARYWPWGOQu6Kl3L9Lz1UoFGLNGgfd97rrrrvoGkVReOihT/KhD32Qf/zC32GYuyjoOoWC7ozd4yIUDLB0+RKmEMIEnmAELViBnnVzrrcf1ZaMTo4hsznCMShIF1p5GFPZkcbJSexRH7pbJ2VnECEXQvgI+sKoF+QyOw4gG1XYGLpJ3nQw7jXNhaKoDmKtemmGVaIrYs5bAsIehfmNNQxM9jrDVAWbr1nOH7z7BlRvqJwQcCm6lOt1OnlLiYIX3uoq4uxmoBVcKfJcceH3hYmMTODqizPHLakLR/D4olwIVSiAT33i3Tz00XdcFIAnhCg7CoCieuKi8p/+AmVOK56v/A3zJxLMy2aR0kLWV0Aoiudzn0H09pfvEa2M8YFP/T7v+eSncGI0p5J7FaGglWo3lvolINjYSPDHP6YoJDm4/I7FEIQNnplJlx63m997z9t4stLHS8d7mJjMkS/Y1Ac9fODWW9laZlyOBKUgQQoam6PEjVGSwmLekmZGhsbpGhnBq3nKhnkhnWIlti1oWV+L8GTJWgZSMUjmcuTdcNe7bmL5pqVcjqYblS9HlZUVXHf9tdM+KRmLp68a5cDeW265hVdf3TXLvZ03u9fjvWwUhqNKOe2m3PrFz4SGgkU2myKkKhjZHLYAoekILLJ2gdxkmqCUlJQ/GxsbN8LMojJOX2IAIU1SySi222awu5OWygWoyuWP5pX2vJQSn8/HX/zV5/lfplkWEkoClhACt8tVcpECEtXlRihBfvnT79FfcFPIpDANna3rlrBqwzoUbaa3r1QibuiaGl62RxAySP+5UcLRZhZX6LhTXdTIRUwBu0sUBQp9p3hy5+vsOdPH6NAAfr+fBfPm8p6bryEUqyVUf+kE9RJd3sYlFBRsgnVzeM/992O4d5GVkqqqGB4L2nsSLG2LcHnknIvpTSWwXpn5XvLes5H0uLjrkx/nM7/zB9x1+xbe2PcS61ctQQp7tiI0aKrqIGBe9qbF4Mr5UzYAQgG00MW2P62mGmpqyo9xHBIqqlpiNtPUT5iKPJ7WLxUF/N5p/19AF4xB1TSsYA2NLctZJOsxCjpCq+P+69/KllWLUZvnF/siikzQRmpelly3hXnzdtF+vo9PfPqTfP5vv8hkVpLO25SiLkr7XlEkoqqB8UU1vHj2Jd72rvt44VdPsnT5fLZ+9vdwBa4MVVLqw5ujEqOa/VtFUWZF8ngzRTRmT6gXSNtGCAXFH+Kum+9g25//A739/cRqqjFtk+XXrkENRyitkCy2E4An1swHPv4Qez/3N2zesIHTZ3vBrWC5os6YZtuMb6J/JSoVLr7UuKdu4jAhEahkfnMDvWdHmJwYI1hRhfRGGDCDVGvu4vOmZgGgtaqCefU1nOsb5NYbVnKqa5KkWiCtNtPoi5VfuqUA9YGBIR75/vc40TNKZTRCvlDg6Sd/zQL7fdx07/uubsyXe8PZUkql6JxG2iRSWWcjeD1MJjO4NYVg0MGQf7Pb7TdJUkrOnulAN3TmzJmL3++/5JrbtkRaNk9t304gECASibJy5fKi9HRxIJ+UEmlZJDM5zrSforq6kqaWubgEjiFbiP+rY78asm2byWQK0zTRdQO3ppEr6ISDfiKRsDPmYr7p9P0gkQwNDnPk8GGqqyvJFXQa65tom9PCjKKoJXsX0N/Xy8TYBFXVMcbjk/h8HubNWzBlmn/TjGkmTa9cdK6zk3Q6zZKlSy9Cov2vkm2bjE8mGOo9T6AiRltzS1HOmlrzEoimjY0i4VxXF4lEgv7hQWzdYMuNNxGOhrFQ0YpGcIqZFBIwDIPOc92Egn4yGQdPrK21pYjpf2F9+P9OmuYbtm36+vs5e76H/p7z1Dc00dhYT2UkQnX17DUfpZSkM3my+TyhoJ9sXkci8XpcDq6eKF3n/C7oBU61n0JakmgkQkHXyeVyVMYqaW1unb5FLj0BJXvSJX7+x8m2bZlMJqRpmjKXy0n7Ktp0neuUD7z1dnnjxjXyB49+X16ukW3b8uih/fL9b79HPnDHzfK5Z56Stn3pBrZtS8u25Ruv75Rv2bxS/j8fuV8mk0lpW7Y0bfuq+vf/V7JtW46Pj8sPvu898t7bb5Tf+fY3pGVZF11T+pn+mWmaMpvJStMwfuN9sm1b9vX1yjvuuElu3rhKfv+735L2Bf36r5Jh6PIr//y38u03LZMPfeSdcnR0uLwXZlv1Ur+ef2G73Pa9f5Sf/+zH5bGjBy+7t/6/SLZty1Q6Lf/6T/9Q3r1lnfzdT31CmpbljOMSY7FtW6bTKTkyMixPnDguR8dGpGWZxTmZ/foL98wl6JK86apzFa+G5JXsS1fR/tSJ4/T29rDphps419nB4iXLitHycCEDLj0v4td4+M8+SMFO05H2M70g7IV9sW2b2uoIX/urT5IYG6DfNECaIKYpvKXmxaa2aTOvsZovfeYj2KqCW7XetAp7ObIsu1xC678zb+8/QwLJp9+9hQa/zYDlwFRPX+fptrfSd/lcnrGxccbHRmlsbKS6tmbGPeWbUNNmIyklfrfCH7znLua3VDJCtePlunrMgFluOvNfRVVZu7iVexa9n/6cgiKNoqN+qprQhTewbYuzZ89wc4sPJeljYHiEZcv/8136n6Mpe4QENFXwsXfczu8+cD2Hu4ZQMAFXUfeaXiLQOSg7d7zIKzueR1UFfX096Hqe2++4m9WrN7BwwRLnviXHhG1jmiaFQgFd14lGo8W0vje3B64eHeISnO9K11K+7spWdkPXGRoc4OZbbyMQCDBnzlx+8bMfX7GdP1JDIi+QWVi1bIWTmT6tLzDlrVQUhVBlA5lJg7AnyIIFiwBlSuWZallUBSRoeQyp4NFUxjIqyYIsKdAz28grSrAzL5cOhMj/eeQxjpzsnOrnJebqiveesSZXN+dXolAkgr+iltFkgXghTC6Tn3lBWQecmuvTp05RV1/L8pUrGB4envW+hqFz6NAhctnsFfdTiaaiCmwsPcOKeTEyGZ25S1ZRWsPyel/1XM18pvOJBFvSNG8hllQx9Sw9QwNYRafF7EdMAApBfwS/143XLcovo+njmu41n/lz+bV/0+S4wKedwytdWpoPG0sTDNpRBlIF7OhcCraNlCYXsQsJA4N9bHvip2SycdxehabWBiqqKnn8ycd54qmfks9nZ6j3P/rRj/jMZz7Dz3/+c772ta/x6KOP0t/ff1XrP52uWuKS0kk1GR4eIhAIEo9P0NLSOiOhWQjByNAg//L3f4NLSN7xoY/hcSm4vX7mLFhU9mZcimxpk06ni3mPTvrH2bNnSaVShC9wkUvpYI139ZwnkcyQFw14/CrqcBJv6gwNtTVES9UJipOs6wY/+tk2Dh05iZ8cW9a28Nh//BvBUJj3vv2tXHvNmrL9wWliE8+YHD8ZZ2QkTqu3krynhWNnk/gDBiuawwSCHqYM6pIzh1+jv+MgwXAURSkFd0Jty1xida2Eq5tn2Hn2Hj3Fi3uP8cj3vsPf/Pkf85a3bEVVL36flBbVMAzOdXUwONjH9u3bOd1+Ctsy8fn8rFi5kttvu4NwpIJFC5fMWrR2+ua4JIJGcRO1945y+twQhYSf1ZFqdh1N0Mtpqnw+7rt1IZdCu9j1yiusXL0KRVHw+ryzSuLxeIJvfuNfmRgdYe2KJUTq22hubuLWW2++aK2nUyI5Ru/RbSiuGjyBKG6Xyng6zUC2nTnVjYSm1eUbT44xMtlJb/9h/IF1NNTMQZgFxuPjBEMVNFTHCAd8F5jdJCMT4/x0+68513GKD96wgHzezc+fe56G46d565YtNNc1zWBeUkrQM3Qef4mWKpNARSXXXHctB/rOcfz46yxZeh1aaS0kdJ86wMl9r7Hkms2kxgZRfWGiNY00tLQ6oRy/AbKlRJom0rRQPW6kIi7yis8kZ82f3/kGJ3vGGUro1HlMMh7o2raD2sZmtq5uw3cBHvzZM6fJ5tI0NNaTSqWQikSqkpaWJk6ePEZ/fy/z5i0AFAYHB+no6CAQCLBt2zb27t3L8uXL2bhx45se31UxLiklExPjPPazH7Nk6TJaW+dwpv00hw/u5633vQ1N1YoeVac8fCE5yeETJ5gYH0fYJvH4JB/65KfYcMtWwpFo2d0//f7OZCo0t7Y6/xc9XB6vt5zAfSFNxCdJpLL0jOQIB2vxV3jIpnOMD0+CkDMZFzAen+RXT7yAbTtxrCfOj2L6XejDg4jHbNavW10OsCtRz1CaZAGidYtImY1oahBbMZiYNEjVS/wXmFAnR7sZ7jnNuKo5kd62BRLOndhDZVU1N737D/AHnQRXBAwMjeH1uonV1/OFf/gHeofjvOu+O4hGI7Oqx+Pjo/zpn/8eyWQaRbjRCw5jNAyT48cPc+zYfiSSz/zun7Fp003TDKPOiyedSnH48CG8Xi8LFy0uBwI78z/1LEvaHOoexZBe3HULSEaXsELG8fg97Np/lq2b5hD0KTPa2bbNV770JQ7u3cPYyAi2LUmnU7zvwx9i/XXXzQg22fPGGwhUApEYp7pHOPXMKzQ1NjE8PMI73/k2KisrL+qTtCVdHR1k8l4Cmo9McCFWvJt8bzcpBEEzT2jBmtLVPPfqE+w9dZi+QZNQxXnQGtHjCVoba1EDEd6yfh2bV7RRDm9AIqXgROdZXt+zH4/Px8P7ziPQ0GyB0d/HwSNHaa6bqk5dltmMNGP9HYwYGjtOjBKtrmYgPkJi+JcsnL8czRdGIjGNAl0HXiI92MOzP9yHkDbJnI3mDvK2T/w+rQuXOi6AqwghmpWK2/fs0UOcfOY5fJaFOxJl4QNvw+MPcPrECebMX0hdzRQChZSOtzNd0DlzfojelABLYkdbSad0+icL7Ow4zOo5VTR5ZjpBnn/haXw+H4ZhMB4fo7qmmrraGkYGhtDzBQyjQElSO3jwIKOjo6TTafL5PIqisGrVKk6dOoXf7yeTybBgwYLyC/dywJiXD4eQEtuyOHT4EN/42ldIJpMcOHSUdCrFZGKSkeFBzvf28aEPfZhoNIoQCt3d3YwnUlRUV6MbOpU1dWheH9/95td56YUXuPHmW1myaiVNrXPKUCrgFF7dveMlDh86zPF9uwFQsdi3Zw/PP/00d91771RFlqI3K5Mv0DcwRHNDPW5/GL9XwysUMmoeRaiYplHG5wI4cPgYE/E4Pq8Xr9cHwoVM5EjHx5moqGVsfILqqsry4TINi3xe0N83Sn29i2CwAmyDTDaLsBTGkgVqIo7EVY5DsiSqKAbMqiq6LhGKwBtwI2yTzMQwvkBRKhCCO2+6ln179uES87Eb5/GNb3+fF154ng+8773cfusNCByVoxRBrigQi0Worqpj/rwltLXOpaPjLMFgEE2DrvPt5AsZVhSrWUspsSzHm/j8c8/y/Es7mDdvAbU1VXz1a9/gPe9+B1tvux2P241arCGJEFhCZenCFn7xxA7WrFmOLnyEoxanu4apaqglnssQ8E6tn5SS/fv28vQTTyKQpBIpTMMgEPDzD5//O77z6Pepqqkur0VtTQ2BYJDBs51oLheNDQ0MDQ3x8589Ri6b5667bqdtTiuqqpbHLgVUNrTR0dUBqVHqIzFUM83YYB/NSzYRa5jPVOCm4JqFGzn22jHOH3uV1TdvoKnuJGatG6PQxHg8zI7DKrVVcebWrkBVXKVNz/5DBzGlQqXPjydSSUWkEre06O05T9zIo5sG7lLiu5Q45egKeDU3Q3Gd7MQ4es8QPpfJxhpvOam7NE/JxCTZnJOTaZo26XSSaMjmtSceZXL9TdTPW0asth5VdYEQvP76G+ze/ZpTybx4K1VVUYRTgUgWQRpra2t5xzvfBoqC4vFw8OhRJgeGqW6Zz4tH2gn5PKzdeB0/O3iSj37w7YTCIZRp0v3prvO8cfAUtsuPUAtE6uroaj9JLj6AVrsA48J8UwFj46OMpUeosWJEw1Fs3WZyPE4qnyOt5+nt62Px4pUALFmyhJ6eHh555BF0Xee2227D7/ezfft2HnnkEU6fPs1DDz3EQw89VEaovRRdEY/rjTde49Of/m26unvo6x/g3Lkuent6GRwcJpnKcmD/Pl5+8Xmuu24DsaoqKmNVpHWd3sEh8rks+WyOQDBERVUlmWSKzo6zvPDUExzaf4gtW7eWJZx/+tu/5Mtf+Sq7d++mq/04+aF2Xnx1H53d/ex+dSdr111DY9NUcdB8vsDZri4s26amqgqvWwFpoigWk5PjuN0eKiIRNM1dlDokv9r2NJVVMVKZLNJ2JATLzIO08Hj9bFi/mqpYJSWJK2/YdJ0bI51OU1kbw+XSAAfNQcuN4deyNNTXlPsEMNB5gkx8CKCIzCoZHRvF6/GgqSp1bSsIVDhpKU7Uv0ZrdZBN16ygs28ELRAlUVA40tHH3sOneeHlnbiFyby5bYAjhZ45c5Ke3i5OnjxOfGKSH//0JyRSw5w4fhxdz7B+/Uauv/bGMqN/9JHv8Xd//wW2Pb2Dzq5+Tp/t4fU9B+gbnuTFl3ax/cnHCfi9zF8w32ESUpLMGkwkMiyY14yqukklC/QMTnL6bC+NEYtVc6sJ+HwIoZZTNJ7f/jSvvPQ8Sxr8rGwNsX5hFUGvoHMwzTvf8278/ql4qrr6elatXkUg6OPUyRMkk0nq6hoIBoN0dXXx6q5dPPvsc+RyOZavWF5mEtn0BCnbgzcUo6KmCTdgZjLopk3YDcFIVdl8EYtVsWnjjaxdsoYXf/U4lj2ASR02w6QzKVT1BIXciwwOddDasBlUx1Hy/ItP4/IE0fMFUqkME+Nj1EQjjA0OUxWULF+8CrdrimkLDHL9pxieiHNqOE5BuJFCpZAY5ZqGGmKLN6OobuclpGpUty7BsEyyqQlM3QBpkMvnyaVTdJ46xpnDezm+Zye1LXMJV8TY9vg2vv71b7B331727t3L3r37OHjgIIePHObwoYPs3r2bXa/uZmRkmLe//e24XS6iVdXMW3sNPaMjZI+fpCGo4c4kiZ88wfHjh9D7+tG8HiJVVaiaC5BY+TRGcgx/dQPnu/upX7IeFzoL57YRcxssWTyP6nCozICz2Sy/fvxxhgcGMYw8Xp8X3S6gmxaZbIZ8MsvgwCh33fVWAGKxGOvWreOd73wnGzduJJFIEAgEOHDgAP39/ViWRV9fH+l0mmuvvRZFUT5/KcZ0RVUxnc5wtqOTYDiMqmpIaWMYBpZl4XK5yOfzpDMZhkdGWLhoEYFAgI989GPcfsddZNIpfvD975FJTGIU8qi+AJrHzdLFS3nX+95XxqROpVKc7ewinc4ghCCeTNOtSfrHHUNwJpvlP374KNdce13Z4AlOCkg4GJrCthZg2Ta2LTEsq3hwp8L+bASdZ7swDIlL05wClsXrdUMnUzQUO2dEoFp5muwDjJl+pDUH4VIwhYnmUqnKdLIwXD+DaZXIsiwUVSkDI8YqKrEMm5SRZjKTpdq2EEqpzwqLlq/GMk3e/hadp17Zw+hAL329vbQPtDNn2TWg+bEsC02Vl1v7AAAgAElEQVRzEQqG+dRv/THx+ARnOs5wYP8BNm3YzNz5TSxetJz166+jvr5hWo6fZHhkmJNnzqO6nPLoqUwaxS6K8LpOaizLS6/s4q6776aIbIPfyOI+9QxnfEuZv3ABHrNANhigIeZlQ2AMZfwcVFaXJQAhBL5gEFVaFLJZzp03GRoaJ561MAwXhUKhPD9COBJkY2MD73rXu7j++usZHRnm2LHjvPzyDrxejRtuuAHLllRUTrdTWmgDh4hlTRKZSTJyAG9hDJ/I4vMECMXPQfNCUKe2tc/n5/oNm/jmnEfY/syv2bn7ORQljGVZDHRaJI/6WLHSh7JZBWkgFI0HltfyzKlBzmQLCNuLPxgELDasWcktdQVcYiY2ui0EmcQQp4fjqKZONBREkS4msx40VTg5v9PGHqtt4Kb7PkJy/E5O7NtBx9H9TIyPMTI0QiZvEKtxMa+hhUDYUeMt2y5nVJQyAgxTRygQqYhSKBTQVBW32zOlZglB69w2Pv2nf0rPqQ4e+f7DyGwWdXCA4HiCI68dYFBT+MTixbi9PiSSlkof8yts2gcNAi5JMBQkYWapC1dzfYOkMeZIQdM9hEJVsEyL8YlJPF43HpcL25IU8gbJRJpBZXDG2F0uF1VVVUSjUcLhMD/5yU/KKVfpdJqqqionr/IKqvEVGVdb2xzWXXMN3ed7yOfzuDRtWiFUwcTEGFXVtaxevbq8mKXCowB//09fJJFIkEgk0DSNQCCAz/f/UnfeAXZdxf3/nHvv62Xf2953tVp1rXq3LMkFG7lhg3HBmA4JYFMSSkgCARySX0gloYcQ02xsg3uvuMqyeu/a3ssr+/ot5/fHfVuedleSjUnI/LMr7T33zCl3zpyZ78x4CtC8pmliGAZuh+0ZUvMxS6oCQqgoqqD19Gls87cyEV2lqFRUlqDncljCjjg3DAOn043b48oHwk7QysYAx/Ya9KVtq4ZlGUghyGVTLKhyUBwO5EM8TDv+D1ByaVzeSjK6xO2yw3o0TeN0Rxur1i86Y7YERdVzCOaLYguh2Ke/tDCNHMXFxWiqylD3Kcrr5hYsqOZwsGblMhY0N9Jx6iij0UEMXaeyppGGeYtQ8x+jEIJwuIRwuISmpjlcftkVBe+ZTPbiCyqragkGQ3i8AVLJBNlcDkuqGEYOFAeGbjJv3jz7+pwnpxv8XpOewS4qm2oYNXLEFIu5S+ai9+xEddlpYJBjMSTYEQaKQjZrktazxFN2fvpc1qSvr4/KqsLU0kLYFc/nzp3LnDlzWLS4hTVr12JZkmCgiKrqKvz+yamJ7MITrkCAYLiOUP08nMkOIvEUuplGWPEZHQ6VlZV8+IMf58Mf/DhgOzkMw8CdB0ha2Fk9dUviNNKUhUMMmC4criAlFaXoySHeeGM7W9+9Ek0rdEpYOFA9fgyvH7KSSH8vpdX1iEARHbEcs2XhZzbGY1FpJRu23sjay65neGiIgf5+LNOkuKSEyqpqNIeGlJJNmzbi83kBOQ789XjsSvGmaZJIJEgmE5SXl09xyPj9PhauXsY/rPouuVyOY0ePMzQ0RHFxMQsXLcDhcGDDaO2UR+V1c0h0DDMcz9A/MEJHf4TUyABbr5mP4wyvot/vxzIlPT09eH1uTCONiYlTdWHmBKOjKaqrZ3MmjQmwlpYWWlpaME2T/v5+IpEIpaWldqD8OeAR5xRcc+bO5amnn6GjvZ3evl4s06S6ugavz4eUFvv27mXDhgvGE5JNR0VFRRTlMy6eSVJKQqEwt3/6TxnouRLTtOyK5FLmI8VVNE0lXFGDEBMalDRyKP1H+dUjRxiOjqKpGv6Aj6rKSoIeJ6uLWmybwFg/SJbODtOxroX7XziCFBamZeJ0uVk0dxYfvXwZdfUN9sRKBQcgVUnGMnj6xRcIz+qjobKSwaEhuttPcfVSH2np48xRz1u8irmLV545Ssaspvl0TEwHBBNCECwKs2jFurHJOSfS/FyhKlJK3n/L+7jsssvJZnXa2tpIZ7Jks1kiI4NkcwZOp4Nbb7mpQJuVTg/F1U3sffpF9u7ajyZUWvsjLJtbwbyllQRr5k3pb1HLEj7zhS/jEGBgB6wrQqCbEpdregfLZCoqKhoPHp5ufEIRFFXUs3vHblrbX2L2wuVEh7vYvvMgpUXFLLr1RsQ5YvzG3mmHwkwcBgqApaFJiaGV8cq25+hJm5QUhRgd7mV4aICFZX5cxbNRtMKxaNIkHh2l4/QpQoEQCSHobj+F26niq63gXLG8qqpSXlFBWXmh2WGMli9fPmVeziT7pjClSk4BOZ1OWpZMBZbZKoggY5jEBvvJRUZAT6MkBwioJjVuk0BJBZrTVdhOwB13/D1PPPEo/oAPj8eNoqiYhq1hmqbBxRdfdla+wTbCV1VVUVVVdc5nx+isqzw2CZqmMaupicZZs6ZMTFVV9e8Fmhxru+bCS87j2YnfnaqKkstw712/IJE1QZrohoU/4OfKpY2sqzKRcxdP8vlJNEviUSWz161joKcdUzfwh0p5X0uYoCOLOr7wtmKuqi6qa5pI9T/M3pM7GO7vIRAIs3X9EhY3rkN1nlm+nN8/VEMwCdT5+4NR7WuZg+oqO2X2rMY6YGKjn/nsGA8WCtl4lNEj2zk5mCA6MozX52OZdwnKfK+dGaIgNkMwf8F85i+YfxZezs7nuUliqQGK9Az3PbUL88ndjMaGCFTWceWSEMmsg7PV9J0aI1vg2sYSCio6oXCY1MggsbRF5+EjKC4oDlexucSD5ZyaGltPDXPq2CkeevgF5sxtpqm5iZ72DiqLw5StLc9Xmj43zZRh5XzoXM+d9e+WtNMTKV5cho4/3klipJvnf/k9Vi5ewNbFNWR0Ff8k2NMY1dXV84lPfGqal+ZNNGJqbc83y/t0dP5Vfs4hzX9fGo9eZ/J2mhj8maS6vYQWbWDNBZvIZrNcsuUiHn70Ubw+L+uveAelyy8veBNCwd28kbJkmAsTJqnZ5ThVJ9ICNWDgWbak8HkAzYW/roWNW95BqHeIymARp0+3Y/pL6DCKqQwWnRVBb1pmPqPA1DG8GbDdZHorazDeRk6KSROTRToFUkUAmlAIL9zExe/spq6rF2EYjCbTOEuL0OtXTjvsiQwEE5riuHh4W/aOwFlSS/Xyi7iwNUkilaK6pJSeSARPbSW+hubxfG1vhTRAqhrVyy9gzaYjlI/ECAoHkeFBHMVlDLl1tOJ6zlx01RmiatnFrFzRRi6dYdm85XS19TJ/8XoaN7wbobmm7e+PhoRtAXYXBTlpBCirrmdrbQm9nQOEyspIlswhVD8HE2WKwLBNK+S9q/busvF9eYPONAfk+N6f/Hm+ycP+vAvC/k+QtCz6h0a462c/wbJMPnnbZ/B4Zi50OvaRdHV18N8//THR2Chf//od+WyWZ5+INxuelE6luP++ezh27Bi3f/7PKS0tnbGtbfC3+MmPf0hH22luuOl9LF2+shCT9D8ouPI9IqVdY/A3991Nd3s7H/2T2wiXlORf/OY3z5Qe8usxMjzMr3/x36TTaW669YPU1jX8QQ+98+VtMp0PP2+mzdjYn3/yKV5/5Rlu+cjHqZs9DwVppwkSb2t03R8V2dEAEOnr4eFf/5yMlHz89i9OKhw8VXDpuSy/+tEPKC7ycfkN77dTVk+d3xkn/I9rNoXg9VdeItGxn6KiIK+99hoXX3zpWZtYlmTHjm0c3Pc688u9PP/s01z77uuZbsxSSnLZLK1tbTxw//04nS48Hg9XX3M1FRUV0wJdxzbk3p1v8OwTD7G0qYInHvg1t378trMMQ3DsyCGirfto8msc3PkaLctXntd14Q9G0o4ESMRGcAwfp6nYyUjfKYpKSlCQdi70t0G2CCF447WXWV7p4NnXD/Gbu+/ic1/6ylR2pBz3lOl6jmxWx+/zjV+3p3seQM9lUDWH7cSZIYHl5Da6rpPNZtm7dy/PPPMMmqZhmiZNTU1cd911eL3eaWPlZDLDgcMHuf+RR2xbqNPJjddfz+w581A1ZVoeM8kkXUd2sabex+vPPUlj01wQ00dBZFIZLGkVXM/HxykFHq97Am7xvyz0z0nSXsnOE4dYXCLZf+IEx44cZOHiZTMq2qlEjFTPcbRB6GtbR8P8lukfnIHOCUAdHR2lr6eL0vIKwuGpaObfj8bUSwFCopsGQyP9vOuSlQwORxgeHrY1/3z6kGlnQUieeOg+PrK5mYaaKl7oPj3O+2ReJTZy/Iff/wF3/uxnDAwOYloWmqbx3e99nw9/6IN84YtfmHZshjR5+Lf3cuPFLSxsbuQ327uxTBN1xjxdkr6eHppqilm7pJG7nz1GLpueEi7xlmZsGk3tzayHPxhg6yWbkNkkzupqJtLkvS1yC4CD+w+wrFqjsTLMge4eYHqbWmwkQi6nE4/HOdU5yLqVi2xQpFKIyB8bczYVY9/252havJ6R4SH8wSKqq+sLnh0jKSXZbJa/+Zuv88wzz5BIJEgkRm1DvKLgcDj4t3/7N2666SY2b97C2rVrxq+62UyG4S/+A//92jP8ZqgdwzTQhILy4Atc8vUvsPbqK/KBIhNrYUhJfyxGNtLFrOoyjh/rxJSgTpNXS0rJ1z/5t7R2ncZd5CFQWoSiKiSio2RiaURK8C+/+jYVNRW8WZrMk2VZU+byD0ESiWGYtB7Zz5KgxeE9R7nsE6WM548/s4GAQKCITZs2oMcjVFdVjgdsny+fZzUISCk5dvwoew/s5aknH+eJJx4nnU6/mRHlT/qzBfzaf7Msnf7OKJas4ECrRm80yEjURXf3IKY5tUTMBJbEYO26lcxbsQIRruSKq95JOh2z0cRndJNKpdi+Ywe9fQM2rEIIUukUkcgIL730MvF4fEobK5uj9cXdnD50DJfQ2bbtNVLt7bTuOcSZsatj2tlQJEkkaREor8PfMJe+0Tj79h8lkUpNFBqdZg5yuSxdXZ2cPHmCWCw6beCpkcsycPogkYGTRPo68v3OMLVn9BCJjNIZybCrW+dIb4wdR04TG01hGOb4oXD2oOSzvF9KDNOku6+XlmWLmNVQzcYNy1nYsphINGKXupcTAdCWaZIYidLT0Ul362kCDuju7EbP5Wbs48ThPUgp6Tx9gp72E/S2HuDI7qfyYSVT+bnzzju5555fMzQ0QDabySdsVG3PlykZGhrm+9//IXfeeSemaY7P9e7du3hw56tEBwcRQqJpCjWKi4qUwWPbXsYwDHsMANLCwOLRg6f4+rOHGHZ5OTwQ4fFTUR48cJykZU7di4ChGJhpE3PQIHZihMjxYTLdKUTMBi2LyTnEx74jaRWsB9JOBBDp7yebSRfsFV03+OCHPsznPvd5Xnr5ZRLJ5MyLB1P32pjNamyTF/Rb2CbVN0T3S78jd+oweILUF8/GOHCSVCSCVdiJrQVnchzcfZjTp2IM9Izy4A9/zZHtBxno6DlvE8o5vYpLWpbS29vHwEgnr73yCtu3b+erX/3aFLzIZO/B6RPHeOnpR1i0fA3zF7WQSSdwutwUhcuwDdVjA5kwDEeG0/y/f72PXFbBLUA3FEy1l5MnfsMXvngzlRXhM2wwkmw2jWnp3PyR20A1UaWKqevoRhqn5UFVC42iHo+HufPm8NzzL6BpCn6/n3h8FMuCtvY2WltbWZrPjy+EneFy54PPcPcX/g6/zLL3NyeJj47QeqSfJwecfPKuf0dxaQWaoCUtDh4+xm92nKS4eRZHXx4ht3QtT9z/Iv6iYhbOrp3ispcSdu7cwTPPPE00GsE0Terq6qmsrOLGG28qfNayOPbCy+DtJxc3qGm+hHkXb2HyqT5hDM8fGZbk8JEj7DvZRVFJFYZWTldao8JVyoG2QUj1ccHqNeOncy6b4b6f/xenjx/hiuuuJzrSRW9XGwtaNrBiw0XT2hwldrm40x3d+Iqr8IQqcGgumh0pdu/fx5KFiykvncgrL6XkxKk20rkcVZXlZDNZ4rE4ne2d1DXW43K5Cl38EoLFlbz00I946ukXaagrZ+WFl3Ng3w4++vliFrSsLnAIACxfvoKlS5fS2dVOb28fTofLDqPJk6oqJJMJjh49SldXFw0NNhwmGhnh4ZEObg1V0akOY6SyXOMopUvJcfjYMdLpNIFAIA8sVVCkIJgaYiRnsLvmIo5FukivCPD8i6+ytqoER1EQl3vS9yIE5U0V9A72I0Yl0rDycyiRLoHwFhbElVJy/OAhHv3JT5G6gSXBoYA0DDx+H+lkksUXXsCl73vfuCa4fccbHDh0mI72Vn59zz3cfPNN3Pr+97NgwQK8nukTbZ4+dZoHf/vA2EbGymXxaqod3qTYuMYrbryemrr6AuFl9R8j88brXLx8Jc56DxesXMH2+35CY/RiVrz7FhSXa+IqDOx66jXu+tEvSY0mSFoWbqnw1COvs2ztcj72jU/jCZz7ZnJOweV0Omme3cwD9/+Gq666mh/96Mf84AffY1ZTI/19A1x11TXjte/G6OTxI+x+41WO7H2VpUsW0dPZRkXDbJatu4wFS9eiadqkK5y9MCMjSU53DlPZ2IzDpUAqh54dIRLJYhpwpk/ZNA3SmUQeqOrA4/FiSfskzOUyeD3m+MSO9fX000/z3DPPoKoKTqdGJpPB63QQTSaJxaJEI9HCCVBg8NApUukUxaqPkYPDSCxCDj8jio5wFApvCehANBtHzyRodwQQw0NkBBxO9DKSGAZ1akGPrq4ufvLTn+CUadKGA82hcuToEY6fPMHGjReOg3kBHG4PS99zM9//8AdZcflCXvrev+ANh8Glk46O0Lh8Ey6vZ9zDM7ZhEkPdvPT4/Wy96aPoOPEFy5FC0NN+mj0vPcSaZcvGY0dN06Cns53e1sM8ds93icdSqIEAgaIqlq+/ECnHCrXCWAcCMCyJaUqE4kYrq8Pr9ZDr20U2m7PDnyZ9LIZhUNdQSzaTwzAMfD4f2WyWVDLFaDSOCBcVgJQFgvqmeWy56sP09o3gFiO0dvQRKCrluYd/SriknMrqhoK9u2bNau6//34efvhBbrvttjwOcMxDrlBeXkp5eQlbt145XgwXQDcMMrrBrICHb3hnY/kko16NX0bb6TjZSiaTyeMWVRC2GWPz3EoOxRX26E76MmmKPG4+UK5QXlaC8wwpIQBN1xgeGSQcLkZDQyJRFYWcoSNHcljGhHYKkIkn6Nh7EIdTw1QlIX+IVHwUzenA4fUy0jtY0Ed/Tzclfgfu+c0IS1JaWsqtH/ow77j0Em7/9KdpbGycYtsbGR5h/649+dhYFUPPEXI5UBUbnJpOp7nw8ndAXf67QpJLZ0j0ROgZ6aLuyvUIrQevL8LsSzcT7T/I4JEdVC5dn7/aqQhp0dfahQyqONQAauVc4qeO4RgdJjV0FD2Z+P0F1xgVFRVx2WVbefXV1wCLn9353wjFhvof2L+fv/yrrxZ42TZuuZRUOktP2wnue+hxSorDzF1ZzxsvPERN/SyKS6tg3I0qQFg8/9x2fA5JYHQURySDbhkIGQNLIxePQXWYyTs/lRwi1nuIysa15JJxkC4UwMpl0YQ2rcrZ29vL6HAEIU1cTgcIOzG1lUwScDmwLOsMW4wAS+K0NISwULDDL3RDR0gK1XnyH7Jh0d0bxXK68eppylU3mjDZm8wRGU5NYFEnUWlpKYnROInRJAhrHCBZWVFp81nQCWhOJ/OuvA6ngJGhN/jen36SUHkYy8pQv+w1LvuTj1NeX5tXugRIgxXNFag3X0vU7SGetuyKQqqGx0ryVx+5BpeWT0WNwOP18573f5QXnqjE4Uhy/69/Q8DvI7M0xfYXn6G8qgbTFMxduKTA9OjQNAJ+P263h2CgCJdLIxQM0ts/aFeonoSnlfnqPJZlIFQF1eFAsySGYdHV1Yt7OEI4HCIUDuJy5wtaSIHH7ebjn/sb2g6/wL69u5GmRf2s2fz8P77M+z7xNWpnLRyHe4ANbiwK2hlJ7CtbvjYn4HK5+MlPfkpxcfH486Zp8OP//DE1movjRoLegUEsVUGNCFqEk22pnmnNJblMmpHYCCl3BZ6KWkwEfQEXDj2D4fQW1GWQUjLUO4RH9yJjoEudsTxdOT2LkTU407Si57KYRg6PEMiMTjIxgCVAz6TJpEZJxWMY6QwOj23Uv/SSiwkqOV7euZ9Xtu/kqaeeRs9muO/ee3n99W1cc+VVfOCDH6CmpnZCiTB1hLRre5mGhbAsVAROzYFpWUwetRASAwUj0k///kMMDkXpfHk7JWs8FIcD+Moa8PlGKC0LoUsT11gKJAGNs6Cxr5TWg0Mkh0YIChN/qJimxdXkol3IirJ8HzPbu84puIQQVFdXc/XVV6PrOgf278OyJPW1NSxauJCjx47ypS/+OXf87beoq7O1CZ/fz7XX30gqmWB4aJjetiM8+/C9GJbC3gOfYcOWS1h34SXMap5rf6hYJJNg4UMJ1TCaTZDRDfScA6G62HVsgIYFswq8cn5/GSnpJHnyVSQazqYVoGiY0kJTVNKRfhzls5gsJQb6+xiOxRCahlAUDMOgpKSEdCqJx+MkmU4WQhaw7TZS2gA9Ky/UipxeSoULdLNA65IIcrpBb/8g5aUaJcYQwWwMn1tnpNzDqDfvvTuDHA4HzbPnsnvPHhyqAxS7GK7L5aG0rHwKgN7j8fCOm64n2T/EyZdewd0/QCw6yuwWJ0N7n+GhvzjMDf/4HYK11YBdcTw73IczWIHM2RkFdKmhKAYWTlAkiHwwle0nYfbceTTNnUfbqZPs3nGI0cFOnn/4bjI5E1Nx07JmA5/+wiz8k/JfKUIhk0lTGg7jcjqQ0mLO7CaGh0cKkjuCwO310tg8m3g8TkdHJ+lEDlVV0LM5dMMgHouSiMUQso7y6srxlnULVpPTTVSRZttzj3OifYjWniirV7bQengXwUAIX6gMbVL1aJnXPO1MGeZ4YeF0OoPL5Rq/+kopMS1JPDHKOtXHd6Kd9KZToIBLdfAxTxVBnGzfvp36+vrxKFgpJTkJMU8IVSjoDg23ZfL6sMn6SC9lxU3gLITCxCJxLKR9CAphl+USoAoVXegFwldKSWI0SdbU8UiBUASqpmGZOoa08CpeOk6cYDQSocRjKwVF4RLe8e5b2PjOd/HE449x912/RHMoDPQPoo8O8dzDv0JJDfDpL99BIJ//zJKWrUcoqq0hCYFQBRa2B1cpECQCS4LemaJx/XKSMoo+lGXPS6P4RgapW+1B01VyagCf4hw/eEBBZlPMnTeHIzs6yLS3Yigq/tIqSsoaiLYepmTe2aME4Dw1LiEEfr+f6667jubm2ezevYfXX9/GGzt34ff7WbFyVf5EHdNWbDuW1+fnz//6DjraThGNjLB3715GR+MUFVeMf4kKElAwpARNR3XnyGXiGLkcDjPOaCyNajbl7QkT7CqKRvn8C4nFIyjCIidVLF0nqxsoVoLRzgMEy5sKxlFf14AUoCoqbrcHKS1yeg6Px0PKkIWFQfOL4y4P2Se+KclaBlLaV9CufUfZ9/yrtFx2oR1BJwRIiUNTmF/r59hxSPot2lQPai7N0oo6NngFljIVRa2qKp/61G38y7/8E319/RiGgVAEV111TX7+J3Nkb9xAwG9HCfzVl7BSaYaGhtm9cxe6Z4CF69ZOpPOREktC1jDZd6IHragSp9OBompkszZEwpJOJriaBIuQksbZzfzNP36X7o5W4rEYba2ncHs91NY3jUcZjDUI+L3M8uro6RHaewBpIRWF4eEB3LkYiLJJ4xE4XS5Ky8oIBoNEojEymQzHj50knUpRV1dLVVUF4dKSgn3ocHlxuMAzfzM3f+479PYNsGfPbmKGSYnhsPOfnUGBQACfz08mo48NDIlFJpPl2LHjrFy5YmJfAfMsJyfVJNkiP3UVZeSyWSSSE2aGxYYLTbM1eqmI/EEkcagOygNB+nqGEcLC5y1itP0EIl5NutzEN7ksqhCUVoeJJ6MoQp3w60qJYZn4VK8dp5iHiwC4vB7ihsVQbhRLgFPT0PLfmtOQXLBhHb5waHwtxoSex+vnuve8l0ULFzIwHOHokUP0tp/mwg1rWLh4KW73xBVZQ1Lu19A0mydFKLgdCoZpIaWJ1++YKMknQQgLR70P57CDUnc1/UY/xf5aeg4Pk9nZy/ovfQlPIDgu8MacdLXzF/Lyfz/IwGAfei5LTkpGElFO7DnEnA9dd16exTeF4/L7/axfv4H16zfwqU9NwPxn6kgIgS8QYP5i2+C97sKLzngArLwZRlUSdHduI5s5RWx4AKfqxtBcFAWrqWueNx5cPenltkfHNPF4PDboU9q14eL9XbjyRVEn8/au667l5Vde5vkXns8bMQV6zgBLYdOWzaxYtmJyD5hSsmLrRWz7+f0ko2l0y0I3JA6nhnBYDO59CfPSDShjAdDYRlNnop/DR4/TjwM1k8Xjd7Co0kulfxXKNFdFIQQVFeV861t/z+DgELFojIrK8nwywZkW0d7SVXPtYO0aYOmlF+XlyNhGmXB+WEJjqL+daOcgJaWlWAjcThdBJY4nUA3CMeUWO9Z3cVkZ4bxhfcNFM+PqXC4nXX1DvL7ndzxxcBjLMAmUVxC0hnj3hatnbOd0uSjP5+qqr6/Na5hn37xC0aifv4r6+bBm89YCdP6ZMIpwKExxuJjevn47lZEpURSNxGiC7du3FwguVdFovOwSfvbzO9FwkE6l81k+FA45VEqqSti0adMEYjxPw0PDDHQnMdImppDEE91cVBHCW1mLKqYGWf/1P3+FRDw5rllJaWGaFhILr8eDP1AYWrTmos389X/+kN7eXlvzV+xrvbQkHq+HFatX4fJ48gCEwr5AYd7CFuYBGzdeWDBHk6mueS4XXnM9CraWBWMFcO2+BBAuKyevPmEZClpxBbnWF8kO9lC5vpiRjmFKG6roNyNII4PCpESV2PvRV92Mw4jgDkpG+nVKvGGEadvQShqnxsBOR29KcJ1NQJ2r3XRIdTyP3pcAACAASURBVNvWIJBYXHtxM5Gueh59ZR8DA92Ul1fjDwW57ZpNtMzxMl0hBImdWcI0TRTFtrGoqiCbjmBmY1Oe9/l8fOff/51sNjt+ktnXQInb5S5IbAigCUHZnDo++6MP8GwkwMETcVyqg7KQj0YxyJbL3mFnfz2DqU0rFvKf9zxPNhpFUQSG08dAKsBIdIRqMUVuTfSnqVRVVVBVdf74ncKPVCCUCXiEAIRwoIkcAY+HxmIn+3pztJ86gdvrocKnMq/Jg+UptjfnWdZxupizKc8LweP7e9hSX8WOQ6280drHnECQd69uwjQyZy0VWGBX5Bz9MHVPnY33UCjEDTfcQHw0MelaaNu/FuRjK8fBnip8+S++zGc++5lpx6xqKl6PnbHCvgXYCxoLzaI18hqWYeD1B5B6ggXLL8DnD00BAglhF2MtLpn4W8F4zjTmC4GiChYvX8ri5UsLQ7Um8zfD3poI+ZqsvYuCH1JKysvLufpd10xtN97NRF9SCJwaCNVDfzrMkOYinPNQ1KiTzZlUxMMIqdjXzAKPt8AVKidTv5LDLxwnJy2yDoFPTVIWtFDUM28909NbRs6/WUDbjM9LEEKhrLySK67YylPbDjB7VgNVVQ30x+IY0o73E9LiTBSyIhT8gSCHTrWSScVYtnAhx1s7EWaA5jmF2tNY/y6XaxwhLwrgl9PzaEk40ZMmm5JcsGIOUnVx6vQpmjdsQg1NNSJaErzF9VxzzVXc/ctf8dGPfYSf/+peBqMJ4lmLqcXe3x4qDCC2f8pxz6LKYFrF6fViWUlKikNEY1HKq0uoDPsRDtuLc64VPZdgQ0pGRi0O9kV4z9bN7Gu9hzkNVWxcuwLTHZqx7Zvp5608V1ZWxuc+//kz2s6Af8s7RsbCVc4tsCVYkBoYpvfhh/jWX91Gz+AQ9z++g7obt4LIVwafqmbbP85zPNMKpEnvOS8SM73pfJSPSW3lhHC1FHAvaOH5n91Fc9CgsqGEaCrC8lWX4vQGp96SsGEoy9esQ/vlb4il+/H5vQRr57D5tr/C6Z8+i8wUfv43YxXHgal5O4FpWuzdux+Px4WqOUhlM8xtbMIb8GI78AoF11hQdmtnF6aRpa66ltauHooCPqrKyxBiGtRuflCpZIL29jbKSsspKS2dMR7SsiT9w1FQBEGvi6xukkwmqSwtzle4LrQJyfy4YtEYBw4cIJPJMhpPUNfQwIJ5c/AH/NN+DOdLb+bAkJMiExKJBMlMjlQqQ1EwwPBIBJfLSUVJAJfbl78OnNE+z2N8NE5PVzfBYJDqmpoZeZBScrK9h7RukI1HOHTkGCtWLKdlQTNjds9zi8c/DrKvlCbtHR1k0mmampqmFJ0dR3sDiXiKk6fbWbi4me2vbScaGeXKqy5HqMr/+rillESiUfp6e6iqqs6nWZ9JkZBYpkEkHicyPEJZRSUBvy+f8mhCcI0/LkDXMxw7cgifP0gunUHVFJrmLLAVDpgqXCUk02lefWUbp1tP09g4i+raWhbOb0ZV1MmPzzxpZ0dK//GTZVly/9698t6775Lf/vtvyYceuH9K0dLp2nzjq1+WKxbNkjdeu1UODw29vTxJKS1pyn//p7+TW5Y3y+su2ygHB/rsgpr/hwqEjhV2/e53/kWuW94iv/Rnt8tsNn3OdgP9ffI9Wy+RqxfOko89cN//qTGPUSaTkQf275e33nStXL9qidz2+raz7quxAqdPPvqIXDqnSW5YsUQePXL4j6IgbDabkZ/804/IZUvmyc999lPnHEcsFpO3f/LD8l2XXiDv/PH3pWEYZy0I+2bJsiyZTCTkB95/s3zv1g120dmxPgppRtl0zquilJKcrpPL6Xg97nw+73MLxALxet7PvjmS0i5R9icf+widnR3ous7cefNYv+GC8aRsM7Fz45ZlfPaq5ezYcyDvnXkb+cqfxFs3r+Z9G+o43D2I26W+LTMhz2VnehtJ5r3EN25ewU1LK+lM58DI2ulRz9LG79L458/fTEBJ0YOOxAQ5lqr6/4bG5XQ6mNdUxzc/fDUnTrdTWRrEFDOXnB3TomuCgnvv+BA9PQOQSwGFIGibCsOn/tBzogj4wGXL+fqNqzjYZ2MJp+NpgnRWN5fytQ++k5ORPJQE4IyCsL8PCWFxwcIyLr3gIp7ddtyGg7yJjETnjFWUEv76G99hw4YLuf32z5FKJSe8VeegsfZSTv3g3i4Swi4BpigKRaEQHW1tZDKZGZ+XSCwp8dbPp6ftJDVLV+E+Sx2/t8QTgJQU1zQTTeZwaRq6qeQ36++3SdPpDK1t3X/YO/wkMk2L/jSk9CTC4QDVM+OzUtqB7IZwcKo3SWQkhstXRC5r/MHW/w9JiUyOmKVQXRUkMTKAeo59LISgaekFpHOS4rpZNC+YPuOBlPD0U0/y2GOP/qFYn9SXRCgatfPW0ts5RGlFE4lE4synJv2UuNw+FixazEB7O+5AMYlkimmR078HOZxuLrzoHTgNg1BVPcn0RGHg86FzalwHDx9nz/5DJDOS+37zW9KZNJ/9zGdYurSF6eq/ATYeRc8xOtKPVBTCZTWMRoZIRPooKqvBGwi/LafM2DucmkZVdQ1Lli7FoamUlpVNeVZKiSUt4qbOwd4RopbJgLeeeDxF3dEj1AfLWVNbDspb52ts0lPpDJlkktGUwFG5iDqnk3QyibAk/lAIddLRMppI0dnRjiUELpeLmupqTncOUBQOU1vqn8DA5N89NDjCN792By4ly8K583EVl9E4q5EtWzbicjnflnmVeaBme2cfI5FRUroXIfyk1BJ27jlKbW0ZNdUVBeMYE1p//3ffYs+evRw7fIg1i+cQSb+M5vpPbrn1Vq6+5l0FBYTHxmRZFplMBsuycLvdkwp9/H7ayHQfgZRgCdvTNZ39RUpJMjHKL+/8EcePHWGo+zTrWpp55c4nWLrqWW583wdoaJwzxTtumibpVBpVOKjefAuqCtmcjsxm8Hi8iEne51dfeYXv/Nu/cup0G729fdx4440EAoGz2g7fDI1pf1JKjp84yazZTfhq5qJ5g0TTBj/40X9RUVHBLbfckC9UO7YOcOJEKw899RxDrQe5Ym0tD97zGNrjb3DdNVtZs2IxTsf0KbillCRGRxGKgmWZBALBKeOx94ikq7uLX/7qLkb6WrnposW4A9UcPXqUUChEQ10dHs/Mh+MYnVNwhYJeKkpDxCO1eFwa27btZPfuj/OP3/4WmzZvwuXUEGd4+yTQemQHDfNX4shfKwLhUty+IO2HXqVu3ipc3sD4JI8Nyv732CAL+ZgRigGoisKmiy4mmUxSVlaG2z39VSapZ9nR186uzh7mVVQj6uZxYrANR26U0pwXKC/gJR6Lk9N12zA5duDIsWW2f1FV1S6qmgdjWpZFIp4ik8ngCwYRwQCqpmHoOrpukMtkcXts/jo7u/jmt/6JN9oi+IqrKC8NMWteC08++yIXbL6If/3EJQS8hcLoje1vEAj4sQhzvDtKz479lJZV0d3Vw7XXXUlJSXHhWkg5XoDTjuscq803GXZg/x4Oh8eD56WUdPVFAYE/WMxw0oXP5yOVSdLbH6H6DMiGZZrs3r2L+397P/F8taay2Yu4dEkLd3zzDv7p29/m4ksuxe/355HiFi+++CJ79uwml9Pp6ekml9MpLSuluLgYr9fLO9/5Tqoqq3C5XKRTSZ594jGkhJr6OgLBcL7EnIllmUgEPr8Xt9uLz+8j4A9gSoun9+3ghf17qA6VcsOmS3jkhWcxfH4uWbGC+WUVMOXKBAcP7OPppx7D7/OiC43tx7rx+Fwc2Ps6JWE/H/jY53A4XAXtshkdKQWqy0G4ugHTsshlskhLkkqM4guGxme8traGkuJSenr6ueOOO9i/bx9f+cu/HK92c+b6vbRtG69uf8NeKWF79BwOR/7gsOtuKopCeWkp1151Jc48kDqbzbFjzwF+dtd91NZU0dRQzzNPPUsymSISTzAwMEB1VRVjmpQlJY888TSn27tpabmQE5kcRVVuDF3n+edfZM6sOsrLJifQtG9UhmGwb+8unJoTj8/Pyy/+jrXrN7Bg4QKEoox7IAV2gtrfvfQKDzz8HKXl5XzwG7+lrCRMSVmAqooKvvqXf/b2CK662hpuvHwJPxzqx+ebiyp0FsytYdu2lykKuKgsDVFS1YimaTbCXEI6Gef00Z20tZ0gXFKNkOB0qcRHBtj/xiusGs2w6sJ3Qv5kiMfjnD62B5fTLqSqqHnAm6KClKSzOlX18wmFiqf1/mmqwpVXXc1jjz3KxZfMDJAciA9zsrOdwd5+KopLCRa7KM2qDHV1E1hWT9pI43ZMTNpff+WrbNv2Oh63xy6yKewqRA6HRiaTJpfNMatxFnf+6qd4vB47YBwFUEgm03g8HhsBbZpEIxHcbjdur5nfgAJVFVSUlTD84muYgyeYH1jO4Sd2MnLgMI/3nKRY7+K9W5ayYsXKcS2kqMiPojqIRWOAQjadZKC/jxdfeh1Nc7J+w2qamupQFBVFsQXEt/7273jq6edQFAVVtXNRKYrKWOZKTdVQFIV7fv0Lyits4T0wFCWbtYvfBvy1uL1eUO0y6j6Pn1g0TknJJHChqlKkZbnhktU88uoBBkYiPPLIYzzw298ihMnyOTVomoA8RDKZTPDwow9RUlxCeXkFJ08eJ5PN4nQ6iI3G0RSFvv4+rrv2Opa0LCGZSHD/L36Gy+XC7XGRzeQIhIpIxdMY2Sw5S6K4fYxmslxx5RV84KMfYjSZ4mv//I+MGibN8xZyz6vPk42meMeyFfxTZwffuuX9lHk8KHJSoWEkXZ2nSWV0dD1BcciPlBJdN+jo6mPXjp2856YkofAE4lxKUIRKNB7D4y1DCNBUlYypo2ou7KJAE1et+oZGLr54Mz1d7bhcDp588gmOnzjOrbd+gBtuuAGwMWZja97W0cFrO3fgdrrI5rIoioLX48U07XAup8uNqijUV9dwxWWXjQuuEydP8Q//+G9ompNwcYj6ujrWrVnFBRvXUlVWStA/Fsxse8YNI4fD40JaOtLSyVoKPd2dBIJBBgYTDMYSlJWWjNvHDMMgOjDAA3ffxcCTT1KkORhNpfC73Tx877303n47my9/J6rDgaLYgN1cTsfr9VJeUcnyFQu5eItdWMayJAF/gMGBEcpLK86JzTtrQVghxNcB6puaufzSLRiGzvbdB1myeA5rVswhNtjFqbYOBgb6KC+vwJNHq/d1HcPK9JNORBjsPk5n2xF62w8z2NdOMBQiGR/AHyyiqNiOQTt5/BhPPfEkTo8fXc+gKCqDkSyDgzE0p59du/bw0nOPU1FVR0lJaYF6bycH/C7rN17IsSNHufmW90+fyRRIxHvZ3XEKyytYUldDhcdFsMhLYjhDidApDxfhcky4vB+4/yH27duPYZhks1myuQzZbJZ0Ok0qlSKdyTB3/nyuvHormqoghUDJJUgPdmCpbpwuL06nmg8tMvCooJhZnD4/QigEg0G2bN7IpnUr2P7aKwwODqMbBg31NST6WzlxqpNjB/bxu5dfYcumjbhcLupqq1m0sJlEbJjBwYi9eaWd66qjo5Mdr+/i5ZdexrIkc+bOJplMcP9vH2RgcAhFUVBUFYfDiVBtpKpbg5amMgZjaa6//t14ffZmzqSSeFxOJAJFc6JqKulMGkPPECotp6TIj8czSeuQBoHsIEGfh/2t/fT3D2AYOulEgm9/66tct7aJcNNyRL4CMwKGhoYYTYySTqfoaO8kHhvF7XaRy+aIxWMsXryYzZs243Q6SaWSPPng/ViGgRASd5EPC4tsOkkulcJf5EWoEiOTZMW6NcxfsBCXw8HVF2ymc/8Rtu9+g2wuQ1EwyEgsTm9XO9uPHAFVpaKkBG++wKthWbz08F1Y2HPldtmYrkwmzbyFS8gmYlyy9Wo8Ht84dk03DDsGUtjl2BwOjXQ6Qy6bxeV2grTsCjli4uZQURZmw9rVxBIp4rE4I5EI+/ft49HHHufnP/85Ali6bClCwN4jR9hx+BC6lMTTKQZiUfqSo0SQJLAdVKPpNEIRbL3oIlz5/d/R2YvL7eYzt32azRvXsWTRfLweF4ePHOHI8ZPMbZ5dEPITjcXIJWOYUqWnv5dUJkdkJEZNTTWbVjQjR4dobJ5nz5Nu8JWrr+TRe+7hxKkTZEZHKctmKEqlqRyOEBwapvOJp3nw2acpXzCfyrw2OTg4xMuvvEJVVRmp5ChenxdFUbBMi6PHjjM4NMAF69ePCa5vzCSbzivI2usL4vEGuOnGaxga7KW9e4T+/n6Gh4ZYOK+RqmSOdevGGkDr4X0ImcM0dfxeN+GSElKJJLlchlwODN0gl4xhhxQozJrdzFXveg+RyAgvPP1bRhM5ovE0mmLhdBdRW1/PldfeNF4+7EwKhkJ2zbhjR2YeBxCI9vO+ynJ6i5twOT10DQ3zWm8bNZ4A8zP9ONPVCF9J/nlbG3Q6naSzaZwOJ4qZr9psjRn5Ldz5j9fCQEqF0aFuZKoXT9GcfPYIu4HDqZLqPURpYwNSEeNj1zSNNWtW89P/+k9efW0b99xzL6qmUVNVjmHoxONRnE5t/JRzujw0zp7Nxz7xUdrbO4nHo7zx+uucPnGSptmNzJndjFPoLFi+GIl9XRgdHbUDiy05XgRXWhJNUcgZko7BBIZuFoBx3dk+Tp48jla+FIfmQCgSTVXQhEW17MUli4AJsKBlSeK9bQzrKvsOHBrPu29ryCoOtzNfNt7eci6ni3e961qikSg5PUfTrNkcP3aM+GgCwzS5dssWNm7ciC+fzlkIgcPvQ1MFejbHyOk+VIeGgoGQtoMmmc2StSBQFBo/sasqK/nXv/879u4/wL2PPMzpni6Onz7GQCxC24HDdO0/iPtDH+XKyy6zzQ5CcMPGxZxavYaf/OJuHA471bPT5eSiC7dQzyB+t3s8ilACsc4TxDuPE5y7HqfHNe6MskwDK51AuDwFtm0hBNX1s6msnYU/VEwukyUaGWKgp5t0ZAC3309DeTCPCxSkLMmQZaGpFpbbhXC7UPPWi8TAIOFgEU7NgbPAvmKxdNkiyisr+Pyff4WBkRiaqqBgYZo6p0+1suWCtYSLgvm6ihK/W6PC6mX/6BDStBiORykqsgtBL6oN4kpHsTVmOz3Niq5e+nv7UAN+Uj4vOYdGj8fNC5EItdVVxLI5Yn29oOsTph8BXT09jERiNDTUous60jSR0hbUi+bPOy+75puo8gMV5eXc8c2vIi1JR9tpOtvb8Hg8LFqyFLfX1raQ4C8qo/VoF+lMHMswEUoyb9cwCIVDHD96ilWbfOMr6fXZXgyADRs3nYWHqQOSUtKyZDlNTbOZv2DBeK286cgTqub4qUP84uQONi1pxnSYeEvcDA9baKUBFF9w/J0AF12yhcVLWux8WEIy2D+ElrctdHZ1kEwmWb58uS0UMBGY6OksuMLgCCIUQc6yUFCQRg63aqGbFpqdgKdgXA0NjTQ0NPC+m2/CsixiMTvouLi4uCBXFHkbhzcQZP6ihViWSV1NJfHhQTSHRiAYpqisEpdn4hrw3vfegN/vta+GlkRR7YK7hmGg68a40XRybUwVibukgXhWx+G0kKaBJSVuXwCHK43DX2hMlgjSyRS79hzFyOXyZgBQFJVtzzzGFV/7CxTVUWALDxWFKArawm9O85wZ11xK0BwO5rSsJZdNk8tlKTfstNuGroOUKIpCpccBQmP2pHcJIXC7XaxdvYo1q1ZiWRY7duykra0dRVFZsXwZs5ubmDwSS6i8tn0HSBsUbWeU0Bjo72TDxhYUzYGYJImkkSIVH6TY5QGh2NlULUjHo6S6dlG57uoZxqYwZ/5ivvMf/0FsuJ/06Agej5tAuAzVPXEoXLRqNSWhIjQxlus+Xxg2b1PVEGhS4FBV3K4x+67g3vseYMnSJbzv5hvxBTz4vV5UCfH8baGqqqrAV+h0aBT7HWT1HNGRBIrHxaw5iwn63Bxq7WdRWNjrCiiqRu073kH6nvtwKVBqWkgzR006yyqXh2zvAJFMlhQmnmh0bOsCUFtby6KWZYSKfGTSaZKJJIPDQ5w4fpyWBW+z4JpAPgMKNDTNpqFpdn4BJpUoAuav2MhQJEljcRBdzzI8ksAf8OL3uDAti+rZa6ifu2zSm3lzoQuTyL7jOzl06BCLW5bimMHrIQB3qBwRe4ZUeydGYwWz66vRUEj2DmMmMmhnhKXcdPNN46e9PbyJWDJLTuTuUhRlYvxCcOjwSXZ2HmRJy3yamurRVDcHduxkZVMIR6hyEkeT+JuErlYUhXA4zMyUN3YKO6dWVX0TVfVNZzxiawQlJcVc/97rpoxhxjfn7Tw53cRIRImZAo/Xg2XlMExJV3cPFYagylVoQJWYOFUvJ093UL9gBdFI1Lb2mRlWz63BcnqnXePz9RoWFYX4wpf+bNq/jR/m+fk/Mzvv5L2rqirr1q1l3bq1049fgsMR4nTnEDoapq5jWRY5U9Laehrf1VeiOgqdP5pQMEyNJ559kQs2rKa0uIhEOsXrr77G8ioFxeGedpxj/yVUlVBZNaGy6jF2GV9jKZnfNIv5TbPOMUPjI81PisBljHJ83w4cRTXs2bWfg4cO0dXdRS6b41N/+vFJud7yurbDidNXjFS66Rk6hYKCnjHwe92snBXGX1Ob3+MgVIWW2z9NqLEBS9fRVA0zr2ErmoolJelcFm9ZGc2XXT7GElLASGQI3RREIsOYhn3IuT1uLpxXTF1F8XTDmkJvKVZx2kWw/wCAzx/g8mveU4A1+n3CXGYiKSW7du6kurqWpcuWcc/dd5HL5aYES4O9NKZlEj/dTv/Lu/juE28gDJ2ymlq+dNlScM5BsSSoE0ZBVT1TuEz8W50MxJMAGggTzevneEcPz+3q5L5nnkczU1TUz+Xjy0tRGgII11i4xcwf7NsWBzrN387r3VIgcfOjH/yAfmclkf52LNMiVNPEZfPLUUJNKJYJysQcKFJlJGOxes1a4od76R85wPx5i3jPgiBb1zXhrZn3Vs+mcaGuzVic5Hzfc24GTCHQfGF0bxkdUY1UpAsDJ35vmPc21CCT8SnvsqSDx17YRWtO47s//CGKECyY08R75rsoWnHFjIdpnqv8+2Zk+q2hpwRcvGENj977a77z0A+JjgxhYlFREeaTWy9mfYNr/DuR5IW+JXEIlWz/EaLJBMO9bXR3tVJWVcussoU4y+ohH0YnhCA8ezahz97O2fayzcrEoVkSDnDLBQv4yj//lFPxNIM93Tjdbmpqqvny1iVUV5S93RrXeZK0hcTQ4CDt7a0oqoKhaqhCRWZzSMtiwaKF+P1TKwKPFRWYHCM/7rSfYQHXb9jA+g0bAPja1795dt6cAXwtl7GgFxLxBC6ng7Rl4WxeTWDpUhB2DvG3QgKQQiFQ2Uhty0bmRbajZUMEw8X0jKToVbxcMn8lExmW3i6ahMI+Y47e6kEhAE9NE1VLL8AVj/POjUvZ9sZuAmVlXPGua6mrq4Iz8uYrmoZr7ipc8b3MiVusW9zM8EiCxgWz8S/N52tj5i0+AYeZPpPI/xSpAlxzVzJ3cSfSdZwFm1Zw6FQnxYFiFlx0KbKmbjLTALhqGqldspxs7xAtixewe+8BvKEQ4WXrCc2eptDwePPp1+ds4z6zzZjWf6ZGLSUo4SoOxrI0N9czv/kS2+CeSFAydwnVi9ZxJoLfUhwEmlcya1kbw55ThFcsQdFURg2DhkUrCDVPTfAn8g4JyH+nkoJrdOGGBIfmomHFFlZvOEF4YJChygq8/iDh8hKs2sUI7/kF47/9QdYSpGXx61/dxRf/7M/wFoXwNjZTUlfP6LEj9J4+xgOPPcyqVWumNpUWZs7itTde5vGHH+ea665l/fr1TL4a/W+SaRkIYcMizkZS2qWxfvC97zM40MvN738/ixcvedv5l1Lyyu+e5Z5f3UVDfTV/8tkvEphUDPctC663yOcYEPP40cPcfdd/sWjxMt7z3lvQNMc53zk8PMKzzz7PypXLaWhoQNOm1jp8s7y8FSrQpCyLPbv30drWxtVXX1Foaxzvw8SyBOl0mmTGzq3vcjrxer24nLYdSkj1/zP33mF2XeW9/2fX02fmzMyZPpqRRr1bsmRbxV3uBXdjY0gIgSSUJLTkl9ybSxK4SQjhCSQUUwLGAeOCbTC2sZC7rWr1PhpN7/30c3Zb9499zpkuyY5Jfq+feWRp9tqr7ne9/TtrYLMQgn3v7OHZXz3LFVuv4NprrpsSoDvXnI4cO8ITT/yMULCIz332C1PW9797zwEMy0ZyTL759a8SHR7ivo98lGUr1xYY2GyBqLZtc+L0aX7+9LNcf/XVXL7p0oL9dvKw5urzXYsXjuNw8sQJhHOO8HxJoqW1lcrG5RRXLsAZy9Cyayej/Z3ccvstiHPlO6kOI6f301hskhrsxM6xcek98NC5SAgX0dk0shecZiCE4LXXXqTlzKkLer6vt4fEUDNBp5+dr7/8O0t5eeGJR1lcNMzwsR2Mjwydv8EFkhACOwfDNfnnfCTLEo9+/xss8MR4/mcP09fTc95+hOPw/BM/o+XVX/O9v/9rWs+0vF/TeM8kBAwN9PPa4z+g9c0XeO7JJ2ZKO/kcTNnmH/7119z9wPe4475v86GPPcqP/nMf4IBQENLs65bJZPjNr3+BHO3h9L7tZDLnh/5zbIfu5qMsK3VgvIP+/v4LmIugo7OTt3ftYWhoeI6MAtdZY9vWBe91nlRZwjAMAqOnCIydJDHYO8XmPeuYEAx2tnJRQ4gje1/Bcax3JSa9K1VRCMHhQ4dIJpIMDgxQUVU54/funw44FovWXIkSDHLiwD7KyxYyr7aUG69eTFnIM9vrwQZJVrh20yYUY4RsoAzXVOwqje+HvCKEwDJNUskEoeISRocHKS4pRZ1RtnkmtZ/tRMHLokXLztcLdXW1/N5dN+FkxhHFjbiYeOcOZoPIGgAAIABJREFUqnsv9Af3XE9d+ihj2dWUlM5Ms3gvJISgt7uHnz7872y5/hbCfolAuJr6+ec3EEuSxCceegCzfR9XbNlKJFJWeKc0uQRQoS8AiYgu4S33Y2ulFPn/RzG/cyQIej1ctKAOO50hnonP+gwILMtFKiopF4BGKtVHZ08flmOjoSAkh9nSs0dHhjh74gBf+eyH2L79TdrbW1m+fG5EZzcQ1iATF1y1YQU/fvzXnDh2lLq6+oLKONsIW9ra6Ont4+K1a9j7zn7WrF5Fac75k28Tj0b51te/imE7XH7V1ZSXl7N42XJ03TNTYprEYVzzo0D3erjthm2kRroomlcOwuIcApObWVFeybIKiR889xZDQ8NUVVbN+fx0etc2LlmWMU2Trq6uGYwrT7aAD992JT989ghqzXwW1JVw7HgXa1YsYnGZRvW01IY8hx8dH2MoOs7B/aeplccYoZtFZpiFCxa4eXjT+pnNLpKnuUInyMVfBXK5VJrm4eA7+1i7/uJcPe25F9vvD2JZBoZhzFAbYELHTySSDI+MMjoySk0gzdEzJ4jLxUQiVVRWVLxvKtCZ06cYtkJoo1FioSb6z7SzcImXYKhoyjTc9XWjdQzDHb9t22iajqapWJaF3++fMq7jx45z6PhZurq/wVjHYcojK/jyD35cWLfp5OSO8+iJ09AxjF9xyHR0cPy1r7Po7rsINDWhBqbDTgkkCUzTRC+JkM2aNC5bTcz0UmrY6LqaX9XCHqdjcSzDctNHVAVFVcmm0lhZF0g2XFs1Z221d0uOrGAKHUkYLF17UW7EE1ZKGQ0hOSiSSmPFOM54lHRmnGCdlyX1SRf8V2VGRQWBIJ1O09HXSThSSjIRZdxy6OztpLauvoBKVHAQ5vbcyFo0nxqhq6eYqoBJ7+gmOk7LDKxJUlkdmHUO3V0dPPH0M3z2U5/E5/WwdMliDhw6yDVXXjVlHxPxKF3tbZxtPs3Lz/+S5WvX85d/8yUa5jfNvjaALGwcZJIpi7gpGPIuRg8kaBlR8VQJJNUm5JWYwDNwbWqGaXLi9Gl27NnP8y++SDydwVPyJA/ccxd1NdXnNcXAu2Bc+cWrq66gtCSErE4t5u8OCyQcFEWiaslqrrrczxOvH+Fj919NiW8vJzujvN5dzEdzyDCTObdtW+w71MyZrhYa5y2jPzFAT/cQL/7gER66924uWbsG3aMWvC/xeIIzZ1p47LHH6OrqKsT7aJrKJZdcyi233Ex1ddWE5yRne9u78y2KSsLU1NZSHC4lWFTEspUrOXL4IOs2bJyTOQ7093G27RjZzGKGBwepqaub5QMWGIbJ079+kbNtbYSLvFSXhejsH2V/1+tUlJfzex+8b+LDEgLbtibyuXLes4JDIkf5fvJ/plIpvv6Vv2X3229hpeJcuqKcroF3ONn1MOsv3sCH/uDjXLLlikL7RCLBV778T+5tnUkTjcVIJOIEAyF8fh/BQIB/+uo/oufc4yPDIxzYu49IWTHJVIa+dIiWdw7wi0f+gwc+8UlUTZ/hBXMcQXTvOxz4wpeQk3HKGisxgGjnEGO/eZ21X/pflN909RQPkxCCXe8c56nndtN84BAXNer85ten6ftFDyuWLeSmay7iuitXFA6yZRg8/pdfJZvM4NgWiuSg+YMY8RSGYaHpGg9+438Risx0qc9t1phY8/xzsVicn/z0cdrb2+g5cxphG5i7T7J8xQ5WrVzFHbfd5AKrSA4IGVDx+XSKigMEgzqarhHyaKhCyXUwXcyEM+3N/OjxHyE8QR5+tZms7aHlledI2Wk+sO0OFFmZ1s6hqyPKl77wKppWxP49QSyrkRd+3UewpIv7H1qKNIk/ptJpDhw6xCNPPMHmDZfg9XgQuIn8R44fYywW485bbi1gK3p9PsojpQz1+BCyzJEjR/jhd/6dT3zqT6md1zjl/IHkeiEFxLMWh1uTWJKNVr4Wf1E9eMpoHkwgLIU1jSq6nguhyN3s/YNDfOYv/8ZF0JJA9/p56vntjMbi/N1ffB7vLELBdHpXEtf4UB/7tz+G16PjLwpTWXk3Ui6mxXWRAsJ1l+rBMtZu9PHWqT6SaYttV22i++m3KCmvQJ4sNuc8iYbtkLWzDPUPM69+AbFMlgWLFqD4QghVY2hskNqqmoLa/LWvfZ1HHvkJQjisXLmSpib3Zjh27Dhf++ev88iPH+HPP/vn3H//vbkFF5zcv4cff+NfOdU/ykfuvYOb7/0g8VSasdExjrV3UlRVw8JZGFJ3Zwf/8f3vc+DgLo57TrP9hRe4+/4Pcsfd90yNQRNg2Rbtnd1ouhdb8tEbdZA9IVKJOKNjUXr7B6irqQYgFhvj9IHX8fp8SIriGv4VDVlxAw1lJBAgazp1C1bg9bqxUMcP7ee1HTtwLANVUdh7OoptWQQ8Ho4ePEjzyWNs3LTFzfXElWh27dqNZZmkUilMy0CWFAKBALF4lOqqaibDxO/YsYOW9lZi/X0gHLyhCmJJg//40U+oXbSaK665fKK0cY7hSkCs9SzG+BhB3UOyZxwB6JJC2s5gpKaXUnHX6sv/9D06exLIioeeoyoZI0FRicPbe48xPjbOZRc3UBwKAZJr3znRjOXxooUrsDvaiCXGKKuZT7BmHrHhPtLJVIFxCSEYGujlnV2v09s3jG25Ep7u8eDzeaiuDLPu0svxBdzo8DwdOX6CXz3/omsDddzg17JQgDff2suRo6fYfNlGamuqyQO4SJJENmWSiA1j5zITSsMhTMvAo2lzCPGCTCZFuKQM2eMnoEjomoxhmK79WJ6lmVBIJBLUVJej6x4cR2Zw9CyqZk2vas4bb+/kc1/6e5JqgOFogkikjMqycl7d+TbPvLQDDYXh4VE+fP99BINBOtvOYmez1M6bRyCepKOnj7defZXsyBDX3Hkfl19zLbruQVbc/GH33MvYjmA8ZVFeFsSnS1h6BL9mk834SJkpMo5Aw0YUAogkzrS209c/QGmpa6JRVZVEMkb/0DDpdPr9YVz5eyo21MvJt56n9fRxPB4P4ZIwHu9zLNt0ixuQJ0kzxFufX8e00pSEQ1SWBPB7VMrDJS6kkzSpB6FwuuUse/a9TTDkR/MYhEp0IlVldLR3YpoZTMuaGJObU0FFJMINN96IcGxsy2RweJjq6kps28YwTDzT9HO9pJxuW2f1pVeTsRQe+8nPMH1hjp8+g5BMli9cBHV1U+wFqVSSz376U4yPjaHIMl4NkskoL734IjfcdAt+/7RATAkUXcexLGRFRVVVTMtAIGELwXgs5jIuScI0MvR0nsHr0wulcYUQOaYw4VKWNY3Kuvku4xKCkTP7WDm/nOOtg8g5w6+suICrK1YuIdvyBtnUg3iDE65lIWxM00TTNDRNI51O4TgOJcXhXJzRxDpVVlXRuGA+u7o7ifZ0I3l9eP0B4mmT7//gh7y9azebt1zG/PmNNDW5ga+S4+D0DCI0HVvVcnXWXTATxxKke3oRto00KRZrz75DdHa0MjoyQnH5UhR/MV5fANvxMD7UxklJ5sjJPrZunKiXZls2Ho9MLDaEr7KCYGkxSdVDamSAUkUlHU8W1s7IpnnxF4/SP5wkXFZKZXUFquSQyVr09fTR097O7td/y8133c/ydVsLK9DR0UV0bIxIRQWa5sUwTUzDRpVlUskUr7+1iwfuvdPdL9zqG4qiUFRchKqq9Pf3Mzo6SiweI+ILTN7KApV4vSwuDTJqCxRJYdXylfT2tVPq98xhrJCwbAs5ZwjXNX/upc6sqvGVWzYTqapmpLODPc3jHPny/yWge8gofpx4GjudQvJ48fl8CKCopJSqhgXsfvtNJATh4hBjo2P0jI7x5M8eZdebrzJv/kIuWr+BtRdvKIxpLGYQjSWJhIPIko3XoyKhkHIEtpBIGoIir5yPaQIER44eJxwO4/f7sR2HTNbNAbYMc8p3fi66AInLTXtoObqH8cEuqioqsWyHgN8LyTGsbHpGJHGBHIfOlnYy2ctAeBno7SM2HnFF7Ek4fpYEDWqMB7dt5ORAkr7eBKlkCi/jLFuymBoxjm5PiP+SJHH33XcxMDhIZ0cnA/29DA8PkUilSKczrF17EWvXrOHabddMtEFCU3WCRgZroJkhrYlgIIiXUcrLg9ian4rQzBgSRVGZv2gR0bExTCOL1x9AHnVz72xhz4gcNE2L8fFxNl68jp6+QSTZrShREYmQzRiT1GPX7mTZFtmshKK4QK1uiomTWz6HTCaFxxdAzukBZibFmuAoTQ9ez1d+uIP+vt7CQbdtiwfuv5PKM/9Joq8F76KLp4zLMAyEELnSMgFM08A0zVyi7YQqdeWVV7B27RruvOtuHvnRjzh88DCyouEJGLS2nmXPO3t44qkn+KNPfJxPffpP3EwCR5AaHSduZvHrek6FEjiWRbLEn1PVpq7VqTO9xJIOupwlmxpG8/hRVS+OEBQFg1iZKDteOcDWDYvJp5okjCTFkVKSXV1IssZ4NkF1ZT2WYRDXVMx0ptCPpqpcc9VmxkeHGRroo7X9ONgGhu1QV1lFqLgcn6eRqtqGSfeooMRJUVXiY2x8jJLScoTjkEjE8XsU7rm4kZWNVbkz5ZLX6yVcXk5ffy9jY1G8mpdUMs0Tjz/Jhx76MEUlJTNcS/OqImxbu4RfNfcSjUY5eeoEXp/C2rowbuWhqc87OJw81onP78HnDyHJEo4pqK0JoggT27anBOh6PB6Kgh6cVAw7ZqMEynFUGSGNoygwb14Vt193baGm2vyFi/n4pz/LzXfeQ+fZFp742aOFkJTunh6OnjjBokUdWKaZY1wCW0DKkli7vBIza6OpGrLkYAsZgYEQJvFEmqqi4ISTTUBbRwe6x4PucbFNTcv1KKbTaRznwlyLF6QqGtkssiRhO4KsYZBKm4RLQmQzJrbjnKOlw8jwIK+9vpM7brqSM6dPsuQTtzJxhqWcxxB0RWJ8PElXzyCO7Cfg1ymvqCCkOUSMcUJFfoRw8e0kSWLlqpV85zvfAmBkZISx0VESySTDwyMsXbqE+vr6qUORoCRcwuLlywmXhEll0sRjMZrPtqMVealYtISa+lr30ckR8opMXX09Xd3dCMsia5hoXi+KomEaJtNpYHAYv9/PseOnKC0tRzguGtHAwACOA4ZhFHiEsAVpw8KwRaGmkhv34ripRJLEyFA/keoGlBzAq6JqaL4QPj0wI/DQ49FRVQXLcfAUl0+dQ10dsWjcvbUlCXIlekAQDpdM4b+SJBEOhykpKeHvvvz3HDxwkHgizujoGN3d3fh8PubPn8/KlSsKbWRZgiI/yUQUSdGRPB4EDpqi4IsbBGqq3IoUObIdh7FoClUR2LKGkBVMIwGSQPX6qamtpbO9A1uyCxxCliR84WJiQ/2EAn4c4RD2l5KxkvgDQXzBENokNUNWdOqWbaV4fIDS2lEqmmJ4dB3TNEGSKY1UEamqmRa7ZVOW7ueGq6/kyd+8RiIRQ1FUJKQc/mMFYqgT2FQwE+i6zs0330x9fS0vv7Sd6Og4/oCXrq4Onnnmae697/4C0EbebOFYBsfbOkilTDRdJZVNkMrC8+8c5qGmTaiyWmBeEiAjU+7pIeMMMBJTUFUNWba47fZVXHeFgzJN6JKQ+P3772f/gSMI2bXvOplxBA6SInH1lhupLJ+otCJJEh6vlwVNi5jftIjFy1fS2noW0zAZHR0jnoizbMUKVq9ekztvDtGkwVg8RVHIh6a7SNigIOPgky2Spolh6lhCRpNyjjHJQdE9ZNIphGXh4JaGDwa8ZEyXAV8IXQDjkvD5fHgCYcZjKZLJNLF4iuqqCkajUc7n8lyxcgnHjp5EEQ5+v0akvLjArNwoe/cNqYyJ0DyESstIxG00VQdZ4eiJY1QuDCFPU/sm/395eTnl5eWci4QQhIpCXH3jDTz3zNO0DYyiKl6ytkmdGuSSpYtdoNdpEpQsyVRWVGBkM6iygpFNI1km/tpcpcZpgoRtWSxd2ERLWyexaAxFVRGOQSqZIBgI4JtURiRYXMbay25xGblwckZ6XLVbcpnY4jU2mq4VIOVlVUOuXUPf7ufJZtMFIFwhXOlmvPUgtUuuIFBWW+inqKiYnzz6YxLxFIZhFCo3OI6bb+nWfpoZDiJJrjH3kkl5fZPV6ClrJcv4qyOULG4iM5bCcixXDbAMgoEgcnlkyjtSyTRvvrmHmpoaEuMacUPGNLMIwBZZjKxbx6xgQAM0r4c/ffTfSGeyhf5lRUFVFbfyhCwX4OTdB9w/QuFKQuHKAjTcXKEDAOPDg6RNi8PHW1xFzHEwHQNJCKRQgPL6RjK5tJ/J66RqGitXrsRMZ3nt5VcYGY5hOnBg/3Fqavdw3XVXTW6BQGYoliaTtkBSsRHIssLJzl6yloGqTar3lWtTW+vB8u6hfzRJLDZEIBQg9UIJt9z41bzle0qb6664gi997gt890ePkMhGkYWKJOD6bdv4wqc+7dqrZiEJqK6to7q2btbfu++XKAp4qNAGMS0ddC92zmAvZBCKxkJ/F0EdVKnYZZDCZahbVjby1s69ZCy3UoyiqoSLQtx46TqCgbnxDCbT+cvaAEgSi9dtpmHFOhAOjiPQ3OpoaL7pLu4JklWFW65cw65Xh4h2neSTH76BcGnJlCBiWUg4kkBSPZx952VePjXM4Ng4meQ4NQ1L2FKnIy2+Fs1fzGxRuO+GZFlm89ZNrLv4Imwn52J3LFRFKiBfz2ijKNx5971cd8NNTIb7UlUVr3dmpcZSv4IS62fX3r0kEwmMrIGuqtSWB/nff3w/i5oWFr5Fn9/P6rXr5xxvwRM23T6y4hoWZYf4YEcHvzxThBYqQU2PsjSUoUFPUHXtX08B1pQkCZ/PNUi/F5rrwpg2Worn1TG8tJZ/PXIaEXeZ14eu38hD91xO8dqNU541TYuO7n7qa0K0nY2h+YrxemSE42AbNqGgh613bSNt267hUHL7Lq+cAEGZnho0OQl+ssf6XPOZTqO9XZQ2LsV3JorR1o1ju9U0JEmgKAqWDdlZhAJFUfD5g2zYtImisjLefPVNWlvP4POpeDR7BrO0TZOrVi/j97/2GLXlEbp6ulA0hY/edh0eRZ1xITpApNqti/Xk9l1suexiTp3tJBpLoSo6lpDQpkjNEPD5+IOH7uOeD9zsVonNjSHg9+PxeM4Z73netCNJQsKmJtvMjpMO/XGbxqaFBPw+FEWl/fgbXFceo6T2VldyzM1HCLj+4mX85pU6dp9oY15NBQOjcYKaxgO3Xk0wGJqz38l0wV5FVdVy6sqk6U73208jWZaorynluDeLmU5SHQmiSK7gm1uCwouC1fMhUIEgiioJ/F4/RjrNygVNqJrPlUD+C0wrvxGqqk4p3zJ5MrO9XZIkNF0nXDrJxT4x7GlvgMqKCOuWzOdn23eSlBV0BRxZJlBcgqbpqPIE8z3fbOZKZpdVDTO0gMDya9iyuhFVlclkDebLA1RctBbVG+K8m/M+kyTJaEsW4FQGqY2UcCKWoCgUojWaZMfRHu6+ePJRk5AkQWVpEZdftp5jRw+jSx5UoWDbGTw+Dw21ARbUhBgYTM9q3Hb7lOb++3uZuhDY8TGC1Q0I9QjIXlKJMYSwCZdVISQNTyCENsuFle9by0lejY0NxOMxJKCkdCbGgmHLvLT7GLosceftt/PTJx5D0zU+9NFPoajeqdOVQBYQrFnC/Q99mEefe5kVy5eQtaGvv4dYxqFSzG6ykWWZkuLZwWDe89ckSeDYucqxfp579F94dc8BGpoWuNKwLbGorojr/urPUANTq5xISCjFVdx83VXsPdXGrTdcy29e24WRiGIYNthTk/fnHML7nquYbyjcagyprEVXTy8BTaW6ugpNU3M2ltytKPK+EYnegWFa2zuIjuRANsIR6qvKaGyYB8zmH564ZQcHB/H7/TnDM8y1LRNxWf0kkwkiFZUEg8H3LWgxnzpx6PgJWlvbqIpEUHUdRdNYvrCJolBwBgMuBOCOjJBMJSgqLqGoqMjNW5t90qTSaZLpNJIkMR5NIKs6Po9CaXERnly1zf9q3lq+vW3bDA0NE4mUz1mhQeRidPoGBzh56iTDg8OES0tRvTrFwRAXrXKreeYDSW3Lor2tA1/AT0vLWVRVw3EckARerxdJklnaUItt2xRXVL/rr2zq3HMR7g709/biSDIV5RG8Hm1KLKIQguRwP/FEmv6kTSqVoqP9DIokUzdvPoFAkMW1YZAV/MVzA77kbYf5Qc+WqyeE4PCJo8THokQqKjh1+iSRigo2XZKr/jnLhN0c2Axv7dxNVWWEdMbEsi02rF/nlpOZJRD7Qmn6GB3Hobe3l5KSMIHA1OBkAYWAZsmxOHbiFL19/VRXV5FIJEglYjiywtbNm929zK+vOwnAPVO73jlAbVUl6axBNBbnolXL8Xo9k+d+brHvHD/vmRzHEbZjize3vyD+5O4bxWcevEccemevC0Zpzw4saZim2Ltnj7j3luvF/bfdKN7Zt084czyb78NxHHH82DHxgVtvFX/+mT8VXV09whFzt7FtWwwODorPf/gO8fHrNoif/OAHwrKs/8pUZx1XV1eXuPH6q8SD994otm9//pzAoI7jCNu2xBf/7FPipqu3iO9+59siaxj/42CijuMIwzDEU48/IW696Vbxn488KlKpcwHCuvvR1npW3HrrteKuW7eK06f/fwCK6jjCcWzx7JNPiVsu3SjuuPJqcXDvAWE7jrDF7OCotu2IE8cOiau3XiSuuXKDOHHi6HmBht8tWaYpXt6+XXz0/jvFZz7xB6Knu/u8a5U/89N/zvf78/1M6kE4jiN2vb1LXHvVteLzf/450dra9j+1h3PypgsChJ3CmV32fL5mLlc1TbIjg9x+6UZUj8xQTzti3cW5oNOZ4qAiy4x0t/GH160hmzU5uOtN1qxZizqthMrkMdmOzQs/+R5/cNli4obFW799kft+76Nz8mpJkjBTSdbVRVi4bhHe+nDhppr9Bp1DLzwPjQ708Ok7LqemrIizA525KppzVzwQQnDz5jXceekSRLgC23Ldy3M9m5/L75razrYysP9VvnDzRg4dfoP+TZcxf+HsaSBueJ3DwT2v89GrlrNkXjG7XnuJRYuWzljf6ecqT7+LOQnAsQUDp0/yoc3rKKuooK681JWJhJhdDUWgqw5P/OunOXW6GdtInyONbILyidJer++8c5FkiZ5TB/mTuy5h74HTDA8OUF1Tk8vrZNbvLJPJ8vQzTxONxhGOzbZt21i0aOEFrsSFUTaTofXtl/jMtovoHxun+cQxGhsb5m6Qk7YLX8o5JdHZ6d3u+3kBYY1shscffYTvfftbdHe058TEC8sed1SV0upqeof6sR2TizZuRhbOnIzPsS0kRadmXg1Ni+uQ7NkP1WRSZIW77rmXoOZlQU0N19144zmfF0JQXV9PIFRB9/A4nuLwpOTw2eckEIhcqeULnfuSFatZtnIpxSE/ofA8bNOeuw/JtUUsXruR0rCfBY31qNoE3t308f930rz6Wq687DL8eoBrtm6eAUs2nSRJJmNY2JpOzFFo626fKFI9begjIyNEo9Hf/Zxynq6rr9pKQBWEfBpaSMf1285BEjTMX4A5Fqe+MsD8+uCsBn/TNPn2t/6NRx/5Pm+++Rq33XY7f/7Zz19Q1QYJCc3jR9g2Hl3lwNtvIBwHZu3JpVQqyVe+/BX+4otf5Atf+AIHDrxzgYtw4eTx6GzYshWf5mfd8hVs2rzpPN+h+4V0nD3DmZPH3Tryc+zpoQMH+OY3vkl7aztOJkMmk3lP9qjzSlwnDr3D9779TZrbO3lzx/NsuXQDdfXzuOb2+/H6A3PeQqlkktYTx+nu6qO4tBxL1jl+5AgVlWXUzl9EMOTW1M5XYMymUjz9re9z8K232bR5CYP9Qxw+0Eaya5iH/r/PEi4vY4rNQLixQAhI2j760walARtfcSm27UYTS/LUHDS3lnsc07BovPxWdNlERyM5lkXySvj9non3A5YQNPc8yeHm7WRkh0xoHmOGzj1L7mFh6cIZdgghBLbj8OJLv+Wl376MP5ClTBj8cvevWP7cdu5+4A6u33r5jDaGkSURj2JpfvyBckyllGwmi6O5UfSK4sZcCaCno5VfPf4oy1esYsnqdXi8fkLFxWizZPHn3/9uaLJ9KxmPk0ok6I4laO/ooda2CQ0OEizKUlJaOqsElTRNtt5wCx4zSsDnI7AyimGZ6Ipr25Ryz/b29PDFz3+B4ZExFixoIBDwsX79em657faCZ2k221BulMxpfpWYYrMSQpBMJhkby3KgJ0ncCTA0Lkic6mfZwgCBkErI5y3YOIVwQJhYmVM4VjN6gwWWhCLtxhg/hBa4FlkrL9jqmptP89L235KIJ7juxhtpb2+jvaMTEHztn/85B/Yxi40LQWbfbi4J+4msWkhN41JiJ/uw33wVZfPlSLNI23mpNRgKUVNbQyKRuKBaZxdKQrjlnjKZNCOJBL2JFFbIS10mg5SI4/fPbgsWCI4fPYqMe14P7n6L1esvwePzTTlPjnB44ZlnePiRH7P9iV+wuXEBBzMJPvqxj7HtumvPqZFMp3MyLiEcnn/65zRWFlNdvJhsdITXd7xEUVERY2Nj3P17f4TXH5wYXA4vJJVKseuNV0lEhwlXlJPW5hPw+fH7NAb7ukmmMqzbeBnkqmIiYGRwiJ999weU+AI819bjGrmFzc7tL/OBP/5YjnFNUMYwGRyMUxTyUdW0hKLrb0fWdQxDwjAtLNuktCTgqgN5RmRatHd0E/QFCFXV4j19Bik2iDGcxVrSiFZbie6dODAKAmf4BCOdRzngXcrymJ9QBww7AzRsdLEkpUkfCrg34qM/f4rTJ89gSgJdUnAUmbf3v4MS0rnq0kvRtamlpTOZDIZhEyouJ+m/iqBfR1VkJNxUicIHKwS/eeYpfv30L9jz5hvoqko4Usq2W25g89XK19ypAAAgAElEQVS34/WHkHMemfyHNTI8zDNPPYnX40GRXZXJdYrkmHuugqVhmKy+aB3rN16CEIL4+ChP/vCH9HR2YmQzIEHfaJzDp7+BP1TEHQ99hHlNTTMOWiZr4vEGkLwe+sYTVFTVkzYtFFlh4swLnvnFkwz0dZNOZzh2eBwci3d27SSdTPDgR34fXfdMUS+FELR1d/P4iy/gSG76SzaTBdtC1lQc28ZMZYhEIvzhffcTzoFwJFIGb7x5BEfoFFU0UbKlmGgqTcbwcPhYB40LKljc6M0FN7t9mWYPjrkPSQU7UEsiblGkDCFb45iJvejF14LsxtWdPHkKw7RYu249x44epba2js6uLp577jmSiQR/8Rd/wfLly6cEC4NACBu5t4+asnloVavwVQgqhnZjdbajbNoEzG4mUDWNSy7bxOYtV3Dw0CFKyyPM6XY9D003OTiOzfHjh+jv68NxbKo3bARZ5cA7u5FlmaXLVzGvYcGUPTGyWVKpFIqiUVlVhaLIBIqK2b/HZV7+QJ7ZCQb6eoidamHLosX4Mwav7j/EsfFemptPcbb9k3z4wQcJFc1+ac1Yh3PPTOL0YIrW/ihVIR1FchgeSzIWS5J47lkGB3rZcs2NrL54Mx6/370HJRgdHePEkUP4fBpej46vKIyq68RiY6RSKXy+AI5tI8lKYbkdIGsaEAyh5JI3PeEgSsZAkqcUg0UIgSIrOEIhlbHwezVKl2/AsR3Xm4YoqGaTyRI2iYE+Ok2Th5PdOK8e4nMVdTRuWMvw6X0s9m+grLo2dw4kbEtQIZazeeQFxk6XM3ZTjBJPgF+88Axthzu5/o7rKK0qcz1T+c0XUNlUR0dHJ4phIZBQHCguLcYyDMbHR6iIVJMLhUEIQSZjEoslsWwJb1EYKWfTsyxRuOHyNo8z7d2kLZlsKoHQdPp7bHa+uYfWlrPU1i2kpKKWy7ZeWaikmUoleWfn25QUB/B5dLLptGvzcdx4PMu20TR3n+rqJsoN7d/5NscOH6IiUkZtQxOeQBmSLBDZJCeOnaSvq4t5TVNtXQIQqoLhSPT2DnL06GE2bdpKqc+PMymWx3FEDuNRZV59DbIi09rajhCCJx/7ObHxcS6/6hpWrllbCCy1HYfHfvlLfvri8whJYBsmlmkgKzJSTho1TRNd93D7tm05xiVx6PBRenpHaGltpbq6mqamJnrH+tGTaerr6xgeHqe+KkzAq+RMGBJYFlJiBBHaRNZKI2lZMkaAZN/b+CSDEv9mFE8uIFhRCQSCnDx5kqKiEN/93sM8/J3v8vzzz/PGG29w6NAhPvnJT7Jhw0bWrbvIvVBycXyOaWLEkkijMYTHg2k7aFZOsT5H+IeUEw7ctZldyu5o76CluQVZkV1JRp4qp7oXiczyFcspLilBCBgdHaG9tQVFVhhqa2F+Uz2SpJEeG6M3bhCpqGRew4J8J2SzWf7zB9/h2w//EK+uYdqA4xApDaIqEvGkwXf+48csWbYMhMOxI8fZNxZDc2SUUBHlpaWs7JHoTmX4zre+RUfLGbZdfz1bN2/BO82TOZ3OybgkWeKf/uH/Mjg4xOOP/5zDe97E6zFRJIn+gWFef+VVXn3lVTzF5fzxn36Ra7ZdDUJBwqEoGOLI8cM0NM5H13WE4xDw+YiOjRMb7MHMJF28vtxWKAKKvH5U4UZDO8IiHCmnr6V91rSizq5ewE88m8Wjq649DBeo0nEchgYHiESKppyA7NgwfbueYcXtf8injUrK/+RKGv1+LJ9K6xP/RrQiTFlVNfn6IIosk66+hMyav6N+eSdyRGbz8itIRdM4siCeSeA3/Hj1XFyPBLZwWNK0hP1v78MwbfKIQPFUig3LqhHZGFA95ZCpik44XIppu4gyQpKRZbBtE0fIKPmIVQGf+fwXab3tNh794ffoaGvDNGKc6eonFAwgS7+hvDzM848/wtZrb+Kmu+5HkiR0VUGRXFTrfJS5lP9IZRd1W1VVKquq818IVSUBqkp9jEbHiVSUYiVH8Ph8pJIxllYGqQpphfEXbmAjg5lN0B03OdDcwtnmM6zacBklmRFkxw8+l5nIssw9938QSVLYu2c3I8NDLGxqdFUV02Lfvj38ZvvLIAluuOEG/vSzn0eWJEJ+PwEHvD4ftqzhC5ehKQrj4+OkUilU3c/61WuoKivDlWogkci4wZuRcoqKigiHi0ily7FtwcDAEO0dCRYvqCDgm4h1ErEYiT6Fjp53GBjM0j/Qz7xqjYtXXoZz+jBifr5InuDqq65k7ZrVvL1zDyMjQ5w62Uw8nkDXPaxctYqOji6+9KX/w91338Patd9w116AY5hgmCjBEJJHB11HLish292Fkkyg6LPD0EuAz+ejqLgYv9+PIqtMLS7l7snRw0fYvXO3eyYdB1V1LXr9/X1EIuW5i1CmsrqK4pJcjq5lkh4eoGNwhBLLQM9UMzQ0wNholPJ5NTixYXKSAQLQVI09u/czMDBIUTCAjIIjHDLpJAhBMp1mPAdNhqSwacsW/qS1nV3HztLe047PcfAEQ9SXlyLbDm+89DJPPfM0H7jpNr72b19HkeZmT+dmXJJETXU1NdXVrFyxnOMnTvAvX/1HErEoGXuIrCORyppccclqFi9bTj5eRjOS+DNRNly6if6BQdKpAKosI+ckofGuY4ysWEXNsovIV586tmc/thAYtoXtuO/pONWCncniOAK3eujE2MzxfnqP7KFy/S3YlgsUattuu1h/B9mWA7BiKlbf/u0v4GgStiQzvyhEcXExtiShKwqqv5T2Y8eYv+7yiZtNkZhXOY+gP0RZbD7ReAJd1TkunaB7YJhrgpdOeT9C4JEsFtOFcBwELlq0cGzslMUSPU1QZKc0cYTAtC1SqQQ+bwBFUXFykomqSshWCrRATkKTqKuvp6KykqYlSzl59DCvPP8svb39SBJYlk24JMSiZStYsWYt4ALARmNRdF2hf3A8V3I7Z04Vbv4fgKop2Jadsws5KGaKtcsX89qBM/T3DCJJMqoiYxkZGsMezMQYjm1PSRsxxto53DfOCSnMriMnibd1s0TyMHj0Da5d3AS+VYVzFYlU8Eef/CQ33nILe3fv4sXnnyMWHUNCRlVdFfaqa67mlltvK9it0qZJ/8gwsqZC7kKQJXeNJUUB06S4qpJQqAhwyGRssoZEoKgYWYGO9k40XWZwcBBQiEaTJDMp2rsGiJSG3PMlBNmBs/hrLmVp4ARNC1UQC1CEhGQFcGQbKaf65HM6w+EwTU1NNDef4cc/+Qnh0jI2bd5Ef98ASBJ33nUvDz30oUkShASD48ixOFp9LSRiOKXl4POhKzr0DkFJZM64RYFbwl7OBzPnxfdJz2QymQLCuW3ZWKaDrMhomsbQ0AjhcAl+n2eSzUoQ62lDHWxDkcPELIeh6AiprI2j6XiFRaazhUwihjdng4xGx+ns7cURgkQqhSarCAnIut5+gCcfe4xLLrsMJAgEg9zxxx/npkSK1tY2vvuth9HKIxixcRJjUbTycjY1XMxdH3ygUFRgLnoXkfMqq1et4pFHf4ppmjS3tBCNxZhXX09NVZULiipsBBI9Z08jZ0ZBqUeoKrqugyRjmTZ1TQ3EjCESY+OT1lvQ3tzMsEfHQkGxBbIkSNkGTSVBdMVN4pi8ky373iIzOsQ8n4d0xkJV3QoFORkcMxHNH5EC9XZ3s3zpWmxkNF3DdmxkSSYrHBrWX0Lzof25LZzUToLSUJjS0EQEcG1j/RxirITP66dCs7ASSRzLVddsx8ay0sTiY1hO/py5vQgczPQwktAwLQfVdDBVB1tIjHZ1ERg+Q/HGbVMOpq7r1NTWUVNbx1XX3Ug0GiWZTOLzegkVhdAm2dBKSkq570MfRdc1DCNbsPkJIXLM1TVgK4pMVQ7BJptM0HV0HzFbx6tpJBJJN59RlnAsi2x5Mb1H99Kw8SqUPBAwDraV5nj5QnY/+yKjiTSJgWH2d/VyVqthm5hd7G9oaKChoYHbPnAH+/ftxTQMIpEI4dJSIpVVbsCrcNd229atOKbF2bY2NNnNUfR6PG6epSRhODZ3bNtWUIkGBoeIxxMMjsYRVobS0jL8Ph9enw9N9RAIFJNIp3FsBSHypVccjOQ4XmUekimjeF0dy85C1oiSicfRbYE8Q86BRYsW8uW/+1uy2SxHjx4nkYhTUlJCY2MjxcXFOYaXuzQMg6yqIE62YZ08iaXJqJIPj881j8xGeenW7/cTHY+SSaVmBUCWZZn5C5pIxNNoulvGSNNVVzrLGckREAwFKSsry7+cM2/voGzeItKWythYlBFLRwqoFFX68BcFiDcfIDnSjy8UQgjYvfMtTp06WZDgbYRba092k8IlAc3NzUSj4xTn0MVlZHyhICvWrOKbD/872UyGvpY2hsfGCZaFqa6ppqj4/Gk/76qQYP5j0HWdFcuWTfm33OxdEdEwcXzFjA72U1dbg8+jo2g6jiPwaBoxk5xRO99K4q47r6R7rIdfvPwOYyO9lJRFCJeV8c9/+3EqK4qZTsHyKpLROI5Q0DwyVr4UjOXgWA7DwyO5QefZkEDz+mgZThFQu6hrmIeUSGCaJkYmTbFPJxQuLcSkFDyYSLPefDPn7pIlJOYtWUu48jC9vX3IqgaORMgfJBypxVs2b4rkKEsKenKUEx3jHD7VxrzGJuJmGiOepO/0Qb7wwdnDO/J9K4pCaWnpnACyRUXF3Hj77EjKc73Ttm1SyTRFDQsYP3AC07AI+Hwu89J1FqzdSLp1/4zodMeR8bYfwTBiDJ49Sma4m+aXf8W6ebXQOBPVafL6eTweNm3ZOvszuW4uWraMNUuWYNn2xO64Ok9hDPnIfuEILMukqqoWSRsm6PcSHUtTVlaOadpE42m8Hp2du3axfEkdspyPhZLQK5fhWG1IARUHExwHBxszcxZR3ISkarNGtucN8F6vlw0b1s/4XeH/kcAjc8gn87VHnyGZTqAqKgF/kJu3ruS+dYuZCd7nUjAY4CMPPcjIyCjr1q5ixfKlMx+SJNZdfBFr160prNHkdSyc7vy/536RisdYdslVjHd0EQj6XVOO5BZerJ2/kNOn9mNZVj74nYb5Tfzexz6WnxHk5i/nKrjIkkRJcRGWYRZWazq/8Pg8zF+1jPnv0r/wnnEVZ/to8+bwqroaRnrbMMdSdHV3o6oqPk2nrLyC6nkNrL78WtdnMnHeCNYv4Oqtm3jqtzvx+XRKS0tQZRVZDyEcm+mek2XrN3Dm1FmypoXfEVhIOEKgyJBKpqhfMx2pWMIrbA4ePEi2uIvS5gjJZIpAIAAIwrpEreZKIdL0GiEXMPc8OY4gMx5FF0kCZWH6OtsIFofZsuFilixYiT7JCwuuHdGraBzd+Qbfe+4VFBQcI4UWKGJRZRGS/6HCgXhPY5rm9TwfCSFQNA21uBg9UIRjWQjHRX+REQhZJlBcymgBlCX/bgWBymWxdp5++QXMnm7uvO1Wdrz0Ei0lGqnLVzPz+rmA8U/qQsJFqZ6JVD2THEmhNFLK23uaiVSFePaZ56msaMBysrS0nKQ4UkNNVYRL16/EzmTcuUsgkPGXNNB5vJ0fHDfobW1Gtk18xZVc3SRx08XL0LRzG47Pt1cOYPg91FSVUTW/noP7D3DvPffy2mtvMTA8iqGcKzhXYn5jA/MbG+YO4gUkWX5XaWwCQdoWZC0bVdPdoGxs1wnmOGSNLPVrLmNyqdUVK1fypRUrZrxrtqT3uZwN0sTmvit6XwFhc0VZCPiCVFXV8+aR13Ac2y3A5/MRLh2io3+QhpoqFjTUT6iKEqiBUjZuu5lLnnuFYDBAcXEpfQOD+BesQQqEp8xMkiRkVSPscTj4+nO0Rm18Pj/JRIyV1QEWBGWaNl05pQ0SNKxez/HWbooiFfj8AUKhEPF4nEAwiCVZzFu9BluVZ4PAOydNPkAeXSe48nKWrT1JaTTJ2qWLGIklkErKiBfVUyEpM9zQvvpFLLloPUtPtOP3B1ixpIlDx5u5aM0qfA2LZu3zd0UCUFSd2vmLGDJVlNolyI5KPD5KwB8APUhfWmbtFduQc0US87F4wapFLI80suWdLo4c97N+zRq6unsIBz14SudPUXd/1yQ54FVUfE6WgBqkd6CHjOEwNNZDJplmLGkwPNjPh2+4Aic9TkHCFgKpKEKnMp8ltXEWV9VQHSnnWGsHg5lhqF07axLw1D2dAFqd7aOVBXiKy6i99Co2dIyRiMVZtXoFHZ1d9GdsPNWzZyZM7if3N7eHC1jXC8m2qGpcyMBIjD0HmzEsB58/lHMSCa7wV7Bm0bIZ73kvDDxvp5v+23cTj/a+JllPf5dlCQ4dOsSvf/UCy1ct4+abrsbvDyK50CfuAC6wk+kH4EICK6cmhoopHWUyGTKZLEjg0T34fJPqAL0HKWXS3zjX0PIidX5808d1vja/a7rQzIA85dWjfBvbthkbHeOnP/4ptjB54CMfoqqyknyS/H8X65o+h3Q6zdPPPsvA4AAfeuBBysvKpkgk+fPlOAIrnQHNlVoUSS3YpiYennk68v0lEklee+01Ll6/nqrqKibU2bnHNivNcQZt26anu4ufPvITlq5Yxu133DUlRmyudw8MDLJjx8tcfvmWmUU2pw4ORzgcOnqYpx57jLWrV3L7fQ/iUSSE69/+Lwe8CuGq8W+98To7dvyWBz70EMuXr5ztvXN29P6URMj3klvA/AAy6Qw//Nv/RUXvEdp2v4awZMi5OAvPTWpzrp/pU7igNlNWYOLfTMvkf//NV7jympvZdv0H+Id//Bcsy55oy8QheLcfcv4GnHseM8eV78uyLYZGx2lu7cYwzEIJ5/9OScXt7sL2ZIrKO8nmloqOYbQeIDRylj07XqFgZ3kfx5hfr5azZzlw+BDZXFnqyRLBZKY60NfD8beepyjZwa7XXy4wo+nnSwiHlv94llPf/zltP3uW/l+/xtstT/FK7/GJ52dfNdKZDI89/E0OPP8kj3zzawwODDDbrXRBaztLL3nptrf5GHqij+M7X51xSc7cH9fD+NX/89ec2vEcz/74ewhHnPN8SkicOLiTWNchuk/sZmx4mMLF8z6dRdMwOLH3JTyZXvrOHiNvH79QOk/kvCCZSaFICj7v+SsTFjKshATCwasaXLlpEw1FXnylITzCyEkP7y3S9/2ivr5BXn7lTdrbOpFlmV+aL/HRj36Yxoap3sJsNsvI0DDBYIBQcfF7Ln3jOLMDGuSptb2bJ3+1gwMHDpM1TUzTIuj3Ux4p44F7bmPThlWzBhq+NxJ0tbajahpVdTNLVf9XyREC1cqytCJMpKqMqgX1iPeRaeUZk2Vb7N63j9HRUfbu28eLL/2Gz33mz2bFu5QkOHn8OJeums+KJU1sP9zugn9Os2UKIRCSIHTjRqxUGiVrYSgytpVkMGnMSBSfMXfTICw5LFhUT0YPEMgFqU6nsTE3ELuiosLNvngXoqhwBOuWNbGq9HpiWYEtbORzfcYCsE3qvTZFHg3do2BjIaNPfYZ8MBMI2WHdsiYub7gHWy0qgPq+XyRw65Z9+JZrkKwUqWCNC54rX7jl6pxPOo7gX370MPPr6rhiw2YUPFSUlxSqn8K0Q19gmA5IDlklSNO6Sxh8+yUC3hBjlo8iI4uuq8hiqo4ci0UZGRkpADpMZm2aplFdXeOWSp7e5+QFmcSxz3XAjh0/RW//CLYDlmWycsVyqqaB2xqmyfh4lPaWFqrn1dM/OEg2m2XJksWFcINz9SGE4NDBoyxdtohjx06wbt3aGUZlIQT9g8P85d/+K/0DQyiaB01TEQ4MjsUYSxr82/f+k6YFf0lV+dz1n94NDff2ERsdpbisjGQsTqDowipOno/yaz8+Pk7z0AB9dha/6TBULmE2t9DY2IimzSyN/V4pmUjS0tLKyhXLGR0dQ0iCvr4+GhsbJw3KvUyjsRS+okpKpAXoJWV09rdwsqWTJQvrUdSpNc9S8QSmbaKVBNG8OgjBMinCcmnSxTzH3HWvj1B1Lan2NsaEg1BmBzHe9fZOvvG1r9JQV8X1t3yASy7bRGl5BJ9/9vOdNycIy0KMxRH7O0gm27EUFeE/g9PUgBycw2EgSQyOjCKrCpUVVdRdtgVhgyM5BWSpfB8OIGyHbDSGk4KxkV6yQqFiOIY3oIM/mGvjmkMs02RkeIhIZRXJeBRV9+Dzze24ELn/7GSK2Nl22g91EwilGRzvZXG3RaCpgWBd3UzVfBY6t8TlwJGDab7zna+xePl2AkottfNsGmsifPbjn8DnyaPDTB2oYTicOjNE+1CS19/qJiJqyPZrnN05QG1YpbI6yJqFpW4r4Qar/eD7D/PCiy+QSMbweNyKp45tgxD4A37+6q/+hq2zJCiDq/e75URcV7QbUza3MTIai2MYBjfffD3bt7/Mrbf8P+beO8yus7r3/+x2epszvUsaldFII2lULLl3cLAdGwOmOBAChDghCbmQQEK5lARybxJCCSSh3BgCOE7cCdjGtiwXybLVZfUyTZreTi+7vr8/9pkzVZKxnef5refRI2lm7/329b7vWt+1vrfg885gn4QQFAtFRscmWNbejqYq5PI5isUCp06dprW1lUg4fFHT1KnTpxkeGWL9hrXk8nnS6Qyx2Exg+bT86pldjE9MoqiqCxZ0HPdAKgSObZFI5/nRTx/nM5/4HTweF4dTyOWQZAl/YH666QunYBMlmMfU+AQdmzYihGBieARfwO+WfcEr0EJZ/OTh8OLOV/jGt77DoROHWdfWSMA8xcjzL5AbMHjHrb/F5//qL/BoGpIszWxyUvmjGMUisqq66XwuoeBsx8FybE6eOU1jYyNrOzsWMKSDS7j77X99kO3PH2RdeyOWPcyJ7iynvvb/+PoXP0b7ipZZ10RBQS+img5goGsyHmQsTDfsyLJQPIt7NA8cOsL9DzzE9u07qPYJpnTY3TNFU2szf/aJ3ycUcmnKisUizzz9DIZh8sLO3Rw/eYb6+kY2XraND3/0IyW72IJm4AhB7rWTyI6DtKSRyJACngDOC4ewj/Uj33k9zMrXLoTDmbM93PeTn3Pq5GmMiV6eD3jIbz/IhsteZuOG9dx1x22oqks8K3AvQsnu8+gjQwTMIMG69RQKYcy+YXR0omvXQNClMxNC8NQvH+d73/knrrzyMrITIyi+ILfdeReXX/c2ZsMvZqMHHGxGjxzntT/9JLYN4aUNTIwncZI/J3Db21jz5b8koF38gAKXOnFhURMNo6idnDwpaG7SObHjJFrAZHA8zVWXref2628kFJide8hkdCLNo88cJByP07J8NYVMFo8CiuTQPZggkSvS0RbFAwhJYf++fex6eRdFvYAQLtjVtm18fh/gGgvvu+8+1qxZSzweRwhBT89ZvD4viizT19/HsaNHcQSsbl/Nli1b8XrncgXOFlmWCYT83HzTdex+ZS/XXXPFgme8Xg9VVXEGBgapqq7EMi1qqqvJ5HIMDQ5SrKwgFou74NrZc0y4GVCPHz9Jc3Mztm1TVRnn8OEjXHXV5WWlOt1fA4Mj2LaNLIOiudcGW9hYRh7bkAhGK+nuH0I3DDSPSjYxxP/71jfRCwZX3fx2Nl51DZomM9B7DEdU0Lp8mXuyK+0n05EJwnEYHxnFFwxgmS47US6b5fj+g1z59ptRPRrTJCbTbSgWCpw8cZyzp05iGAbxyio2XXYZ1TXu6dQNMnffMQyL++//Lw4feA1Zljl6YgRZkpA1GRmZZ599js/++Z/h9XhcJeo4pM4fp5hLkM8bWAOTDOamiLYupXXpSiobl87p1/mTWFNVPKrGxNg4nZ2rXdbyeZkSBCDLCr195xGSxOnzBZAElmWTzxVJpzMLyoiGo4wd6cXTWI0juZACNyLDVTrT2Rhmb3KJVJr/+/ff4vjxk0iSzFhBQpYV9u4/xOGjx7nyiq1cd9U2JCSE45BKJPF4faxatRrDchifSvDrJ37F2PgYf/LJP6WltXXmdF4qp5DIIp1LYDsWgaZ6hLcKBwe7owX59DDFiRF8gdY53q7v/+g+nnjy1y5IV1HwWOAInR07XuDlXa+yeeMGli1tdYtBAtvCmBqjkMtR3dQEUi2+iIOVK6CbOlFLRwgfSC4V3s9/+lMS40PsfvZXSBIUTUHfmROcHxjkjvd8oHxtnz1y+bzBUMbEVDxEhYU8OEGV41D0yIyaMpFzY6xqa12wHufLRRWXqqh89o9/m4/dcwO/fPYgL+49S2qyjrbmMM+88DLZlJejRyf5gw/cRGtLYwlLrJDL2mRSac53n2VVx2oUDaprahBmnr4zp5GWNJNOt1IVdhd9f/95rr7qBn7wg39G86jIkouKThXyaKqGIwRdXV0MD48Qj8cBwcjgOYp6kYnJKRRNwbZNhHAYGR3gwEGJrVuvLIezzJdIyM/m9R10dXVy770fpqoqvnBhaCqZdJLGhnp6z58j4HOp6oP+EGkzydjYOJOTCRoaGohGZxBKZ86c5dvf/ieOHj2GbdlEIhEsyyIai/HYY4/xZ5/8E1pnJWXTNA0hwCgW8AiBXsy7HIY+P6aRB+FQGQuVbCEQjNZw5+/cwzOPPs63//Zr1HX8G10rBW3+AoZSye5nGmnbfBebtnShKR4EAssweOjv/4EdT/4arwyhYIR8YgoCQQrpNP/+9a/xVz/8IctLdGPpVJIv/MX/YmnbSopFndcO7CedStDU0spAXx+mqXPo4AG++nf/SFPJQ2XqJkePHyabTeH1lcgYhMCxHCQJlIBM0TbxUUohKUsgiqQHzzJ8opfJ8X6G8ynsPXl8d30Cp1AkVNeAP7QQ/ZXJZnno0ccomgb+UJBCQWddxzr3JDdPHAS6biIhl1JDg6qp5SD0OWBjyaXoqtm6jnQ+45Lc2i77jm3b5PIZvF4PHs9ce29PTx/95/ox9AI+fxB5mhVbgkI+y4GDh7juqm0guQDZLVs6mUxMYRZyeDUPsWgd+ToMCF0AACAASURBVHye3rNn+fLnv0Bz6xKuvPpKrrjyqnIcoWoXMXKTCCmAkrKxZFcpy1NJchWgGdactiDg1KnTpBIJCIdY3tCAcAQTRQPdKKIXCpjmXPJVIVw6OUs3sCwTXzCEaZo4lo1kOWSGx4hFoiU7uiAeDZGOBgh4JPyqRK5okk0m+M7f/S1P/vJJfvvd7yYaq2B91wYqK12Wp2w6x8GT3cjCptKjgaQgyQ5G0eC542fQzvS9LsWlfPnLX77Y778ci8Woq6li07o2brqqnau3dmDnUjRX13Hv79xMa62H0yeO0djchM/jR0iCx3/1CpMZm/6es3g0D5FwpETtZXJuYAjN66W+vpLaKn9psng5eHAfu195xUXeZ/MuCWcmi136W9NUPvShD5d2Ionm1qW0LmlDUWXyuSyqx0NFRRWWrmNaNoqq4PV654S/TEtvTy/HTpykrqaWgN9H14Z1C72Qpf8PDQ0Si0RJZ1IEgyE8HhVVU7Fth6effpqf/uynXHPttfh8Pmzb5pvf/Db/9Z8PuiEZxSLpdJrJyUk61nSw59U9PLt9O3ff/a4yHVhlZZQXd+3FMNzJIhwb2zQQAnzBCI5Z5D133sKa9jY3Nk1WiFXWsWr9Rprb2uiZOMeQ4+fEhOCJET+nJ7P8cuer+MJROtvasEyLx7//Qx79wY/wOAK7qJMen2BodBS7UMCjaUxMTJLP59hyw/UoioJeLHJgzyuEIzESU+P09PViOQ7JbB5ZkZE9HizbZkNXF1XVNUiShG0YjB3azvnRSfLmrKwWkoTsWNyxbRnr1mwgEq9x24GEL9ZItGklwco4Zuo4tX6D6hCYY4OcPPgijp2jakknsuIqbSEE+Xyenz3wAIcOHyaby+DxaIyMjBKPV1JXWzvP0C3hOPDIL3ZRNOTSaReEZWBaBl3r2ljR1rLAO6poGrquY9tuwD4IFEVCCAfLNFE9mkvgWyro9MkT7Nv1Eol02sXByQpIbpD+piVVvH1bJ0s61rtKU1HYdNk21m9Yj24YTI2Pk0ql3IB+vci5gQFeeWU33adPs23bNmpKtlfZSKEXTxGoXwWhCLJlIw1MYJt5tCVBpNoKtHBl+YadSGV4+JHH8Pt9pDMpvGE/KBKyphCLxEinUqxatYqO1avcvpVALxTIDY+DZRKqqHTtWQ4Yhg52EfwRAjWuoV5WFNZvuozlHevpGZoinbc4NzZF3oJ0wWJoZITTx47zwgvPsXHzZhqb3A3Oj0Xh5CvEdQsxmSh7fSUc6upDXH7LFkK1S1wvtCR95UKK6ZJB1gCSohAOhwiHQyxtbea6K9czNjbGoYMHGRwYYsWKFfi8gfKEmUok8HhCxOOVSJJ7VVAkBcN2yVB7+85x7ESUNcviaKp7BH/ppV1uTiXLQlEUTMtCkhUy6SwSMo0NzWja3KuArCisXt1JQ30jqXSa/u6zjJ7vo7KymnAohK4XCQYXBk/k8gUu27KZ3a/u4cYbrrtgu6f5GkdHR/F6PUSjEU6fPs0rr7xKJp3Gsh3++BN/TLTE5Tc4OMj2Z7eXg3+LxWI5Lctrrx0hkUwihEM+XyxTm61c1soX//zjZLJ5nFJOKFGCQUiyRDAY5IqtXeXThOuVlQiFI1x949tp7+ykZ7CH3pFJnP2HCPuDrL2hg46WJYBr/5s624PpuLupV5KxNA1JWDiqB2wLn6aiZ/MlHkOIxmL89T98m1QqxZnTp7jy+puRJdANk4qKCtZ2rlsQYqRoMu11lSAkFFlyox1kGZCpqKpkeW0j42eO0rhitTvtSvpF9Qaobd9MrHEJmeHT2KbLQVkrZEKxCmQxk57IcRz2HTxIKp3GsEwaYvWsW9vJw4/+gv986GFURWXN6naEI0obnGBsIkEun8Ywski6G02nKgJZ1XCEMz8+uTz+gUAQXdeRhECWVdfga7txrafHhlhR24xPdZmnllcGee/WlXz/eYN0roDtmEhCIej301gdIyS77n/N6y23u6Ojg/b2dl577QgPP/ggu3buRNU0bnzb22lf3U5XVxer13SUlYqkSWTsILLmwVdKdyQiHkTSIZ/O4vXMzd918OABBgaH6FyzmvMDA4yNJwkGw5iOjUKOdCbNczt2cPe77wBsJBSyiVFS46+iKQ3IloFQfFi2jiJpZDP9KKEk4AKiZckN+G9qbubGm99GX28Px44f5/jxE+zY/iyO47C6o4Obb76Zro2bSzYvgYJOe4NEH1CwLRxJAeGgSBLLa/xUy0VmzfQLyuv2P84oDPd4XVNTy003v63s6p+2qzil1Aa5XA6vz4eqqni9XmzHxjRMGhubKBg6puEGZCPcQWxsrOfQ4QOojoqNjW6IEvZKRlVVNnR1LYLNAiSJaCxONBansamFbVdfhzIr3GExA5+iKHSsaWf/wUPccP3C+Lj5ba6rqytjhDZt2si6dZ1uZgVZnmPjikajtLUtY3R0FH/ATyFfRNM0wpGIS4keiXDdddfhK0FLJElC01Qu37rxklix+a2YxhPV1jZSU9PAVsfh3Te9w81zVb4GgaYq2JJLL1eUZHL5HI6q4uBGCGRNE0dRGRsdxTBMPH5/ubRoNMrmLZctqNsCr5cQmKbNVDJLoagjHIVpglIhC2LRCqrqWzD1hcHD09/yRarxhqvmfLO8cZZ+JssyHe3tnDh1ivZVq9h62RZWLmtjZHSckbFRBkaG6WhfNQd6ohhZNq2O8eRLx9HzOTSPF4/Xw0dvu5Wta1sWKK1p8fl8+DweclMJpIowMirZ1ACqP0ZNKFqi2cN1ojg24xMTTCaSbtnCAckmnU0xpTeSTqcxDb2UbGDGPqYoChs2rKezcy3Fok46nXJJbcNh/KXY0OkyhG5R1boW+0waPD4k28JRQYoE0SKCoK+yfPUVwmFsbJxsLscLzz+PNxDAthxXEcsSBd1gw4YNWCXmaEVWEBJoPhWheYm2LEcvCjTVBhQsUaQoDBpLQfjlcZnVjmVty1nWtpxbb72NT37yk+Wfz7aDCsCyHXKmgj7Nqg4IHBxhY9gWedMgNu21u4jyelOxiuWA1tLEdjssSUtjI8VkP6Y3iI1MIZ9DiACmA6lsjqaGGpqqoiilysmyxN//wzf467/5WrmyMxPKHejZdqTF6uImF5RRyjT2iz/rOA6HDh9h3bpOBgcGGRoZY9mSltfVXvdvBa93bpbRaYlGo/zLv36PfD5f9opOu9AVRS21I1JWXIt9/42IhASyjLYIwlvWVD74lS/y7s/+OYhSVoiSGb7kvESRQfN6LwiNeD11U2QZLV5LsKKS7Niku5E5Ngg3hXagYRlmLoF0EVDXYuXMucYhUVVZyQff/35My8IyTaKRCMuXLeHO224tb1iz36mqqeDKtQ08tQuyiQQVVdVIssqqxjAh7+JchJT6JhyrAEVGklxGc399G15vYMHJ37JMTE8QCVxPuPsvhGPTsXELBb1QPs0u1j5VVQmFVNfzeAGxbIfJcyc5a4TIZ0ex83k8qTz1uRyVlkRIvmbWN915oMhu7jLHcTBtNw+8pmmEwkFuvOF6Dh486GbMlSwkAYqs4gtF2XXkILnRNIVCHiEkauIBYvIEy6MXDtafrcSmYUuLisePN1bN85KXoVANeiGNX4VwIMDG+jrWxusv/O4seUOK60L2ICSJ+roKtqwJM/rC80xJrQh/K0NjI6gIipIGIs/mpbU0hfIgSYjSySEef3Mgt9e78CVJ4q533s7ytmWsW9tOLBpZFKX8estYTHldTMn+j0jZe7j4L0PRGKFobDo7DOW/ytFGM9CRN0pcIRBMDg25xA8lw7wQAq2U3roiGsUqJt0g9tdD+LkoJsltZ2Dewrjism0zGKt5r8meIFuuvYU1T+/j1akpfuuWm3nupVfJSwG8kQsTf0hIqIpKRcxNSjjrgDWvnq6iiDWvQNGO4Tg2lqGjenx4fUEsScVXUuILE+G8ftEiNSQjTXz3Jz9lfLCXSNBHER/33LCety9ZiS1LJd4sd0uKxyKsaqknlTeYSqXRdR3HsXFQCIZCBDwy1WE/krBxLz4C1edHjdXxvS99lqNnezAtC9mjsm7JEu59z29hSYE3HdysaH4C1Q2kmqp5zRAIiggtQDAa5fZ1axC+6Otay29xrKL7glkoMNlzlrQpGMwa2JoXj2OTz+sE/CorGisJqRBpbJ2DH7JtB8MySSUT4MhU11aXDLmzy5gJw5l9ZJ1edNM/v3jj5wNM3py80cU++wiNEBTyBRJTUwSDASKxGJIsX7CG02UapsnUxCThaJhAIHhBT+rrlYVtEdi2w+TkJBUV8UXzP4F70hg8cZDu0RRF02ZiYhyPx0u8Mo7H66U25KM6GqaitW3G6zarTEPXmUpO4vX6qYgtDra9YDaECwIeKWuas9099PWfZ+nSVvrPD7Khcw3xkp3uzXWZIDc1zvneXgaygmRiilwmRVV1NcFwjHgsSpXIUb18JaovsGAu27ZNMpnEMAyqqqoWnOZm2i3IZPIcPXmWYj5DLBohky/S2tRAQ1M9mjxDNOFyDYwz0H2a0VQOxaORTqTIZXPUNjTi93tZ0dKEZuWJt65w1w6uLc0xTfYd2MfA4DDJiUmE5FDd0ELH8jbaVqxCeRMRHNPrs6Dr7Dl4FMsyaamvZnBkHFlWaG2qpaV1yWziwgsXNlsRLPLnDYvjOMKyLfHCubPiS3ufEy8N9Qnbti9KLGnblti54ynxkTuvF3/43luFXsgL2y4RVpbecxxHmKYpDMMQlmW9DoLL//+LSwhri/v+7d/ElZd1iQ+/790iOZW4RF/ZwrIs8S///K9i0/qN4jOf+rRIJC7+zm/aN9P1euA/HhBbN28V3/zGN0U6nb7gN6aff2XnLnHPu+8Uf/WpPxaT4+OvgwzXFvf95Pviw7/7dvHFL/+5MC5Ahjv9rKHrwtB1YVlWeU5dtG2zfm8ahrBMa86cml/GG5lHtm2LqalJ8Vdf+Iy4+73vFN3dZy/5Ddu2RTqdEu+5+51i46b1Yv/+/cJ5i0lnHcsRmUxW/Omf/rF423VXi4fu/5mwbEvYwrkgcbLjOOLpJ/5bbFu9VNy8qUPsefXlt2RNOSVi3mKxIDLpSWGahUt994K66S1NazNfzKJJ909/zL23X8NjLzzFhrt+l6DmLdvdSmDusm4Vkkx1yMfdV62maekSsAoIr6+E652rgKdPWLY9Kzj6ElunWGTHvlTYzm/y/JsTQVTK8pO//SNe3L0fhHXRpyVAOA6h7CBfuedmjnb3kU2MLXpNdRyHdDpNKBS6ZFTBfJkcGyV58EW+/O7r2Hl4J0ODt7By1aoLVkoI2PP8k/zvP7iT53btZaC3l4rKi5sBMpk0pIf47AffxQOP/ILjx19j3bqNiz5rFPMUcm6UhCRJLlkGEAiHkeeBe6dl2pZn6wZHnn8FVVNp27iWYEVksSLQdZ1du18mk0njsv+4Nq7Gxka6NmxadK5JksTkxChBM8GXP343O3c8zdKl97rlXyDGUZIg4FX5p7/4EOMTE1SG39jJ/WJiCwvZLrK1Pso18Y2c2LODO+5+L7IoI5TnSmlu+DWbf/rsrSB7iYUCF23HbyaC1NR5DCNHKBxHQiZS0cBsLN3rkf9ZxeVV0S7rwNLTNHkgWSwQ0NwwoWlEt4Obx8sWAlkIaluWo65dg27mGB8YpGZVDFlIFwzUna1c5ns75j83NnSenjOnaVvZTlV1NZLqueBgCCHIZrMc2rePlmXLaGlpBi7GeP3Gxf2ezI233EZq/xOsaK0nmU4RilZcxDsqIckSoUCY7PB5rrzmemrrF6YrEUKQSiT4+hf/kkQ6i6QoOLLMl77yN+X0Jhduj0RFZRVrO9dgjw7zrnfcTEtT48XbL0EaGdkr44TD7DlwkM7NWy7RfhgYnqBqUyvxEHiUmSvPYgrCwab7V88QbmygccsmF3pxseu6JJEeHmP8bD9dt1xDMZsvX48Wa8nLL+/kM5/7NLIi4Ti2a/BHIRqKcdc738PHPvqxOd6yaenrOUtEc5DtIunJ8RKb0UX6SoBuOgjZT3XIQ8oQ1L3Fc0tBQfL6mZpKo+lT3H7XB8p1umBJjsPG9e0UnQPYjkN0Wd0lfHyvT6YdQpIQgIZlGqQTE/gCMTTPxZMzzpdLAlDfSAUdIUgaOj87fZDjBZ0qr8IBxcNr2RwIh9ZgxSxckhuwOTU0wM+/9nVeeeiXjJ7rxmMpPPWzX5A+fpZAVSWhmuqy/WZ6Qs/OunChU9d0gGohneCx+77Ljsf/E8ZPMnR6H1ODZ6lpbUeeHypSCnn58b/9mC999i949eWX2b1nH48+8hiKqrJ8eduC54VwGJ+YZGJyssxsMpvg8kKD4jgOpmnw3798gmPdfTgehYIW41c7dpPPF2hpblqwSIQQ2I5NoVgkmZoiNdDN0jWd+GtakWRpDhxACMGBgwc5NzLG+ZFR0pNj9PV2MzI+xbXXXrsoCacoBScXsilsPcmpQy+RyqaobghS09aOYdio2jwCWuGQMyy+8/Bj7OruZ2//OKdSNt25IlPpNJtXrZzbD6Uy+s5184sn/wsjP0ZLzIuhedl/upuhkQFWrexckFXD1AvIikxN+2riTQ0lz7ZA9XhKwbmLeJSFoJDOkhoep7qtBdWjIZf6dLFxOXbsGM/ueBqfz4eEiuWAY0kokoRpGlx/ww14vd6S8nOvLhP958mcH6I+JlER9eP3NeNTFAIhBUn1Lxi/QrHId3/wI3788/+ir68Pyavwf3/0CPsPHaFrXSeBwCx7mHBdD9nkJAPdJ5gcPk9yYojE6DCFTIm0RFVdoG6pHMdxyOfzDE9N8tCjv+T5Xc/TGAnx0skBDnefo7GhnoDf75L0Tt9eEIwcO8r2L36eqdNPMHzmLIWpUYb2v8Kef3+CmvZ2/PH4gvk8Pf/zhRTpdAJkBVVZqNgFLrepzyvj9QYZHxvC69VIp5JEK2oWG4+vLBicklwyrY2h6wgx7TZe5GSCQDgCVVPnoNT3Tw6zZ/Ac/kiEhx2ZomNjjA8SDfi5un5p+X2nZNDf99SznNi+g4ASwKypIHmuF4wir+3dScIocM/XvgKzzHaJySkefexhqqqqCcRr8dgG2668HJ93BiM1W87v384ydYzYqjr0bJYDJ08Q8soUJsbY8p4/wReKzHmvv6+ffXsP0Nq+jsl0iv4Xd5JMJkkmM6xdu7Z0Apt9ypP51re+xQMPP0K+aHDtlZfT1bmWZcuWcfvtt+P3+xe9okmSxOj5sxw9eQohyQxoKoZlIcsqPT2nuPqKDXi9s+ETAse2OT8wCEgsXbeZpmXLsWQf+XyBoq4TDAYJBlxAcCqV4uWXd3Gu/xwBfwAsE5jgscce4/LLL+eOO+5YACEAQWJqgu6DD9Ja42HjtjoUTzOKIpMd/DXnx21ijdfQ3LKs3AaBxKHTZ3hi124sRSWpK2CDKnR2nzrNxx2BrMwtAwT57BR79+xmbCrN6ZEkXq9CNjWJx0mDeH95zN3NxHCNw0LGdiwM20GypRI+ScUbCJQU3cJ56gn6yWdy/Pob9xGojbPulmsIxWMo8kJFF46EURU/uu4i5h3bIuD1U1ER4Za3v41IODJ34SIYOn4QyXAQ2mqGExpVVVFsfQrZjrAYJmnf/oP84L77URSVvQgefTlA1rA4dW4MJJmvfv4z+Lwzm4NjWbz05H9QSE3hWBaZookkHMLhALZtsaJzK13X3DYzR4TD6b5+/vQLXyCbylNMTRD0hdkz0I/82ikef+Ipvv+db9LRtnTm1uLYPP31v2b85V34NQ1FUxCyill4jYItcCSLd/3zj0qKzgHhIBwT07I4ffYIPWcP4BM+iulxbr3nz8vRIbMln51ipP8Y0aoGNFkmGAiQzxdITo1RUblIgPkF5JKK66uf/jgD5wZBCCRcRLLL0efitizHwRHwoT/6U2669c7pfqO3kMawLCo9XkzLIlZZydiZU5zs62WydTVVARfR7pTGc+D4KYqGTWNLJT5VxefzkDJ0RpMpaicm0XM5vKV87Yqi8OtntvPMM9vx+HyMaTF0HVY8/gs+8oG7aV/VTixWMWeqPPriYZTxSZZXafgkm2xOJ+bxcu7oKyh2gdq1VxGpqqe6bR1IEtlcjlNnTqMobjqQgD+AqqoMDQ3zve/+gHfe9dusW7eWQGDGNW9bFi11NSBJnO/tZuhcH4VCgUOHDnHHHXewYcMGl2NylgITQLQixsoljZwZmELICqrHRbRHY3F0/IRmPy9gZGSMVDKJ1+cnGgnjq2pEyCqOqWM7NqlUkoDfhyTJpVAi92c9Pb34fD5U2cGvyfzsZz9B1RS6NmyksbFxzo5nZLqJ+E1sJYDXF0IJxHAsExBUVaSRSc6fLaQKOTKZDJ6KuHsSUmSMYh7dsd2dfc7TrqxqaaWzNsKLuk3GcMgYDtUBP53VfiRp7vVPUTSKxSKmYSDLMvl8nmPHT7Jz525WrFjJ9TddT13dzOSf7ufM6AS/+MI/YoykKeSKqB6V88/u49r/9Ts0rl/FfKVSEY8TCPpcsC4SiqqQzmQoFnTGxiYWrBMHGa+3wMCQRaQqRjAag6CHTOoUsHTB8yA4e26QQiFPMBhB9nqxTAcPElOTo+x4cSeT9/4+jQ3TVzQJQy8iHAvHMVFUGVWxmErmCAZdztJ0cmpOCZIs8czzLzE+msTr0fCE4hwYTaP5AphFnclcnvv/60G+8tlPo5bgKaOnj4J/gmK8Am8qC4aJjInlSORUh3CTD1GukYSZGmHq4COcdfyMDI3gNwTjg6e48o5PIEkK+WwCry+MPCsNlsfvxywW6T+xl8rKahLFLNG6ZhxRLB2Q5m+ii8tFr4rCEV9++Kc/ZmhkjHw2Rz6fI58rkM8X0As5Crk8+XwOxzbZePnVtK1aXV6Me8aHSVkmXr8fSZYRkkODUyQarWBzbQsBzQPCBVgnJyZ45WcPMzg1gmoLwh4vpmVgFIscSyQIFnXaLruMWG1t+VgbDvk5m3UwTYdsagLFNOjBz/79Bzhz6AC6XqSurq4cob5sRTsDWcGx8xmy6TRNMfeKVDAczg8Ms/2Z7fzi2Z0EK2pY3tZGNBalY/Vquru7OXeuH4/PRzQaw+PRGBoaYs+evezfd4hkMsXqjlVIkkR3dzdHjx5Fwr3GWpZL8Lp37z727t3DyZMnOHLkCLW1tcTj8dJ11yafzzE0OsHg6NRMRL1w8GkaK1rqCATDSNLMgE5MTJDNpenv7yMQDOP1+lBkl8k5lU4iCZloJIysuMj+pUuXMjExiaZpDAwM0NjSRigcIZVMc/ZsN48//ji9vb1s2LChXL5RGOPcqf1kM3kqKitQQy0IYZObGkPPpPDHlxEI15ZOK+6YHz7bzatnu5G9Ppd93HFwTJ1wIMzdV13pmgfmnFTALkzS3XeGCd3CsixkSdCxrJn1rbVUL7+iDKacPZlNvVjqO8GJk6doaGji1T17mBgfZ9PmTXOed4Tg5PbdjOw8iGPYOKZLQ2brFv6gn4auVQuuo5Zl8fTTz5JMpkG4efX1ok4wEOCLX/gSkWh01jXObXui9wi955PkdZtAwIfPHyAg5YhU1SB5w/OuVg5f/dt/YPnSVpKpDEK41h/LKLJ2zSomp5K89647iEVnnAeOcDjw8nOMDA2SzmSIhIPkcnnyhQKFfJ7qumaWtG+Y045/ve/fOXPqDEI4LhelLGFbFqZhYBkmqqJwzRVbiZR4Eq2xU3hyhwjUbSVx9ATT2YcMbKo76rnunZsJLL2izECv+CKolcuICIfimT1EcidorNIgfY6e3lP0vPwIsYZ2vOG4O+wSSLKKxxdGxiE7chbJyuHkcqQSoziWiWOD1x+cxuy9sasiknuVs2wHHAdFCCxsJMlGk6VSEkkJ03TKEwzAdBwKhkEgGMB2HGRZQTJMrmhawcbmduK+GYSwEBLjA+dITo6wtmsj2Z5zjE6Mo2kqfp+Pa99xJ97zJ8CYmfSSJNHc0srnPvoB0pkC2w/sY7DnHMeGxgljUxmLMDk+RTqVIRwOIUkyDY0NfPRjHyOdTvPCjh3sfua/yWWSTCWSGKZgUvfwOx+8m1Xt7g7s9/u56uor2bptK/v27ePZZ59l50u70HWdZCqFPqBz/nw/U1Pj3HHnbfh8Xn73d3+XdevWUSgU6Onp4YknfoWu6yWCUonDhw9z4sRJtm7dSluJvl6WBGFzAjHZj8fjdSexcDFtualBpN4XkOvumTPxh48dxMol8Vc2lD2rsuRea3zCZPClHYSvu5mq1hV4PB5aWlr43Oc+x8TEBA8++CD79+8nn8+TTKXp7++noqICj8dTTqUiHIdk3zBkBEp9CMtyUK0MWDmEbZE4N4Vj9RKrXo2qTV8HBFubq2iuqWIoq+M4DpqmsiJSxb03uafY2QBMyS2I5PljDKYLZaIVx7aYSEwRrr+W+SchSZLQPF5kRcW2TDwejbe/7SZ03eCqq7YxPj6xwKBvF3SG955ALxjouo1jOahCQ1ElhvYfJ3fXjURqSmQspdfGx8dIp6eQJIGsAkJBUbwomoeqqqq5dQJko4hpy9TUN5NI58jndXwFh0gwiKSoC9ohcNOaX7HtMo6c6C4xt7v1vurKqxgaehhJWfhOKBDEjsaQJBCORE08jiy7YXbRiqo5z+Z1g4l0kg2dHRw7fQLb9qAKBcMogu1gmQZne/ro6T9HY4nBXJUFqu7gkWQsx0SW5HKURXXrOhTDxkhP4K9oKPexP1qDL1LN1sZVJE88TW74NWw9j2d4PwGPSr7vVSpqm0H1IkluxpmK6gZi1XVkGpaRGzpKIZ3BoykkBk8xKfWzYtMNaLNinxeTS3oVO7o207Si3U01I7vXtGlgn2WZOLaD7TjU1DeV30nbBkOTY4y+cgRh2di2wJEEheuuJ75qbliJhU1F0MvaK5fhoZHeiSRjw0NYtonskVkSCXDZe2+ieln9HKO8qqpllPqHm24twGqUQAAAIABJREFUgyRdlmJoaWkhHA6X3pnZgSORCLffcQe33nY7I6MjnDlzloqKCurr66icJlAodZgkSXg8Gpdfvo1t27Zi2zbj4xMcPnwYQ9epq6untbW1TLTh9/u54go3t9cNN9zARz7yEbLZLLlcDnD5KEOhkGv0nVbCQkUqZEhPjmHZVUiSwHEcbNsin8/hjdYzewQdx8Gv6PhiQbIV1di2XYaECAHBYJjUcDdnDgSpKjEETZdVVVXFvffe6xL6nj5N99mzBPxB2lYsp7W1pWyoz6WTHNn+a+rbmgmGQtimgVMYw9F1FEXGUQL07t5Bc9f1ZcUlsDELKcxMBiudcye85qWmcSWblzSXvMezbT2uJ7V/eJzzUxlMyyIc9mPoEgVbIqMvZhlyxRcMkUlOgeMgy+DzeZBlmdaWRmzLRNVm4kePPfsyJ3fsR5YFCFGCg1gUizZj5/o5+NDTXPOH7wVpRq1WVlZxw/U3kUwl8fv9btyi10djY2MpvdBMzQSCfHKUoQkHW5KJxqtwkEklE1RENITsYb7/UkJC83j4xS+fQJTGVAg3cHpqYopQKIxnHkhXNwVaqBorrSMBFu4pTJEVZFVhRdeV5WcFgomJSQqFAuFwFL/Ph+U4GHbJmSW5oT/uPJsJRbK8MWwljFHUsQWYjrt2bFvgD8bxRCqQtYXhPBKgReuo3vZBqkvXc8eZte6kuUBqSXLtlJHKJiKVjQjhJu7M57MEgyFXaV3Ch3nJ7BD3fuozpa6e/tY0kIGytwNEyRDnFuZ1BOuEzpm9h5lKplGFhCcQZKC6EfuKK8oxjuBOZl8kSEvHUp78yW40DGzJwaNpyLZEQOSoW7UBXyw858Q1d2d1FWpNTQ01NTUXbfD0O7Ii09DQQENDA5e6U0/bfmRZpr6+jvp5WSovFAoky/IlQ4AcCSTVQ0P7Rl54cjcC12PoGEVuWB0nWNM653pl6DqDAwOoWoBguNnNDSVJ2NOTUFbRUcgXFg9oliQJr9fL2rVrWbt27SJtcI3/meQknSt/C0PvASmIsCwsQ0dybJo7NjF45MwcBILsQG3QT2R4hNRogsTkKOGaBtSQD1PfiGeezVwgME2dl/bsZ3g87SqATIZgNMRkIsGpE0do37x4bJymefD5gxRyaSzbZWlyvbMmDvIcxdW4djkrbt1KUbcRwikhxB0koRDyaoRb6ubUS5KguamZL3zuiziOm61DkmU3KeICJ4Yr+fwEuw6+jKrWI8sqAkE44KGjbSNCVhcE+jgI7r5xC0/tOc7x/lFso4jk2JiGg6XnuWHbJqLRuRizcMjPjXe8D8d25jgTptefz+ctrwuEw/jYBIqAE8dPUDBNhKygqcrMM5IbX+qd1Vfhxnbs+HKSL+8FRcaZ5mqRZVSfH1+0GjUYWzggZfvlzKn1EtSkc9YuCPyBAP5AYN7vLiyXVFxzPVqvTwKazB1tbaSv3cYDjzzFB+55Hw8//iRHT5ykUCgQCk2TorqU3KoiaKxoYstHP8Jwbz/RVAaEoKIyyJr2EEXbj6Z4UGcprNnhDRdr7G/680vJb/reJZ+XwLJVMn2nGRgaZnh4gGIhS0vrMuJLHZDc/GOzX1DVAC0bLqMg+V1jqyVAllAUjXA4SO2yFRStxYkaLlmvkpfXcCBaXU0+MYCwdRzTDdhG8uKN1OCJ1ZVDUUACWUaNVHHztVfww588zK23vI1Dx84yMjjM8MAoS1pYcIQSRpL2FWt45uROUpkkkmFRlGxUScPQwTKNBSePaRuqPxhC83jIpJLkcnn8AT8enw9/cG6gcvXSZm773B/OdPYFothmLyRJYkFm24uKgMamBr7944fp6uzi5puu5cH7f8qaJRrNIsKqzq45j8vI3LxlPc8f7yNQU09mqBslEKUi0szVK6rYtmXLnEBlqaQYFiMCWUxkZKqqqmmrraMuFODVQ0cQwkaSBLaQ0DQ/S6prsJCpKV0TJUDVvFTW1xO/7Xbq7vkY5wZHyWTybFjVRLUYg3wCCYfZ3v23Qt7IWvwfAaDKaEQaO3nXu2o4cryHtmVLue7aKxgYHMB25kbk2xJ4w7VoyzcSmDSJRSqor69haHSKVc21yJpJpLUFmcWBmJdsdAkvlJ4cxzYNPB61zIYsHAdZUVAU1TUkCwfFG0DSvG8KbLcYKPZCz0gI5MolhJtXUF87jN8r0dzQRNZ26PdX4oRqUJjhalQUiaqqSibTeZ7e8RRej4famhp8wQATExNUVlRwWX0tefuNtkBC9XhZtnYTJ3oT/PqZU4T8HiricUzboWgr3HqTRW1tLcqsbVUg443WceX1N7JzzxE2buqiorKaiYlJlPoOFxw2BwYCnlA119x8F7vPpRmdGOaPPvh7/OSRh1AVh203vgfNs3DTnOlbt57x6toFdq052LkF/f9mRnYRkSDe3ElH5xTNza/S0dnB++6+G8u2eHT3fq71t7J8zXrUWUQpkgQ0tVPdepycNEDTsm2cnypSFQ3TvnkbsZa2mfqXQlwco4hVyJXWj0BVShyHquaenjwBmLanSRLVFWG2LKmmZ0jFXLuZbLGAqeeIVtbi8flob4jgUWSWNtWX9Ll7Wqq9+gMkTx7FFDIhyUt7QzXxkExEXopSWclsJutpWRhhMt3/b21XzylhsbCWWfKGYhCmv2noOg//x7/hQWLNlqtYubpjDmB0dhDs7AIl5m/Os6+qv2ldwLZN/v0rn2K05zRrVrbQ1FBHdctSJscGqaqsIVJVT3piGL1QpGrtVURWdL0uppELl+lOtu7uXnbu3Mndd7/bBRTOA+3N//eFQowupfwWe2exf/8m9acUEmLZNn3n+rn/vh+zYuVafvs9dxDweVz08yxP5/z2lK8kF6zPtDKWyjjB+e8sRts13bfDw8M88PAj1FbGuf2224hEIrMWyptfMUIICoUC+/YeYM2a1VTEKxZ4H0utKK+S8tVyTp9Ic2ysMz+/8HiXW1BSXJZpcOjhH0JyCNt28KoqAa+KLxQjWFWN4vcQal6D0rAKNw7FXUGiZMqxLJsXXngRwzC56abr0TRtbnmSu8IWW49z6oar38rrcVZ7dL3ID370fWxT57bb7mJ52/JFU2n/hnLBD/yPnLimJ+DJE0fIDRxl6+WdnDmxlxXt7czGaZQHZ5GavnXKWpBJJSjks5zs6SPkU/B5NfzxSjyqQiGfY+T4UYrpSTyeAOaJ/fhbVuPxB95UqceOH+eBf/4WZiHPQ7kEd3/0Dy6Yp+hS6Pr5YlsmVil9jKvhF1eI0//XcwVGh3uJxisJRSvLqZAveo0ujaFjGjz2w+/hGx7keF83Wy7rom3VitLGvriCfH3tmQF+zvZIvx57o0Dw5K/+m0zvYUZfS3LFti1EIq7n9q0TweP/eT9n9u7mWUXjzt/7A7q6Niw88c+268yjn7u4mcIdp3w+z8GD+0ilU6xbt4HGhiYkabYbQyCMIq/t2UdA0lm+rIVYfT2FbBo5GEQN+Mklp9Cik/hrbSRl2rkklU9sO196kR0P/gTdMEhNjPC+D35oUSW82Hpc9Jn5PSUEO57bztldv0KSJXo6Olm+fPlFv/Nm5X80VnHfrh0MDZyjWlvDT7b/imtve597LXuT8X7TBsh8Loeiqnh9F6YzkiRIjo1RzGdoamoh72jsPXicRDJBR0c7Xp+PWFAlG2wjPTqMP53AERdOMPd6xdYLdNTGiPurCNXGUC9lrXydkk4l+eWj9zM6Ps6Kle0kkmmyuRx1DY0sXdLKrheeJh6v4c533VOmLzu+Zy8Djz1E0LY4Fwqw4T3vY13XxgULbb44DqiaxsbVK6ioDpHT/FTHo6VFtVBBwsJT01stQriRGsPnerjn7Zt59dARzpw4ztJly0u30cXLNHSdQr5AJBZlbGiY2sb5VGbzRWJFfSUrNq/m9MgU0aBv0VXrxrTm6Ovrp7GxEU1TGR8fp7m5+YIpgIQQjI2N8Hd//3/o7e0hlUqgGzrxeJxwKMJdd72bW99xBz6fFyHBYG8P6XSek8NDZLM5tgY0MpkcMg7RihDj53pQYrV4bQNVWYhW90uCzoZKVEWiwbvQafNmRZIkzg8O8Y6tHciqyisvv8iNN96Moi50TLxV8qYV12KTVAhBJp3ime3Po0kmo5KP85MpPvWpT3H77Xdw3XXXEggsPNGUkc6pKRRVQzg2wXCU6Xv7zD4lePThhzDyaQKhMO1r1hEKh6mta1gYlCwk8pkc6AYdnRuoWbqUQ79+iIDf74Yb2aCpGssvv5V9j/+MdHIKWTgLTi7TZ8TFSEEXu9J0rFmLffYQQ+eGaFi+uuTpmrHPvFExzSJ9PadJZYqk01lSyQSpVAqvz0ddXQ3FfI5z/ee55bZ34Q+EAMFYLsUTps3QgSPkg16eH0nyz9/7DoHg4oGtM2MqmEhMcfL8EEvIo4Ur0YVFwNQRqlrKYOtKNpNhbGwMWZapq6+fY0h+qxTYdL3ODw5y9dVXEKqVWL9J8ONHH2NlZxetFwgaF0IwMTZOz4mTXHHTDYwMDFFRVYVnMabpWcNbWd/EmTNHqautoba2tmwvnX0VFEJw4vQZvv43f0vXxo0MjwyTTaf5/Oc+S3v7qgX1ca+gef7ma19l3/49pQOO+82pxCSJxCTf/s4/YtsW73n3+wGJYiqFTxJEKqsZm0rSffIMdU11xKrjYGRIjo4SHTqP066DJ7igIRu2bkU/fYB8Jktdx6ZZXnl4S67VjsOG9pUkT4/QUBNHP3MEYRigqHP6662USyqu6cli2zaGYbisJ6WdTfNo5fvyfAX25FO/5vCJXhwh8Xuf/i7jySxCGuDZZ5/j/p//nM1bNpeV0fT7qcQEE6MDyIpCIBDG0PNMjg9RVdNEIBydfpBCIQ/Cpm3lag4efo0n/vFbbNiwjuXL27jiqmvLcYHTdjSnkMMnQ1VTM8N9ZwgF/QQCQQoFg0AQHFklcfYgk7kiaiZJemSQWOsKXn7+BUZGRphMTJFKTmEWdBRNZdnSpVi2TTKRRMgSH/+jT+AtpWO2bYex8Sky2RzDhDAq4mQdjf7zw8QrIoRDwfJp/BL2xQXi3uBkDFMnny0Qr6ghUBOkvqYR23awLAvh5AgE/Ph9fsDFhE2kMtTWVJFvW4U+MUr/uQF+8tMHeP/730VsGqoxvTGUqqQXi+zY8TQ7djxNYmiAfDiIUYBj//lTmhta6OzaxIqlbhI6x3F49OGH+MdvfAPbcbjrrndSWV3DypXtXH3N1TObVLkMga7rWFYpdc8sg+b8PlFVFZ/fX37PNE0+85efp64ySl1NBV7Vy5HuYT756U/z8AP/gTrLCzn7W7ZllVJqu5vPiUOHWXfZ5oVKDoGpm+z79QtMDA9RTBZI9SdI/fghAhUVLFvbzvKutWX7jWlaPPKLJ4hXV7Nn715kRSExMcHnPv+/+epXv8TaNR2lPp2pywsvPMfBg/txSXTcnPXTNHVSCUf47PZnuOnGW4hVVGDmEtRVhLn8hls5+Mv7EcIln5GQMB2oXbGWzOgI+uQAnkh8epmQzqR45ZVdZLJJzg0NIJkmPc/8N839vTQ1ttK1YROy7PaHY9kuSYsQpTUuFjqZJPd6r07jEEs2NCev09XRhbzOvUr/9fUeJAeEZSGpC8dj9qm8/O1543UpZXfJWEWEYO+ePfT2dvPaa4cp5AvYto3X66Nj9WqWLFtG57r1xCpmGy8Fr+zeja4bKKrCeAZQNBQEt9x0DT1HdrNpyyakGbsmmXSS/u7TtHduxFParYWATCrBqWP76dp6fXly25ZN55p2evqG2fnyAXzmKK/us0gXMvT2DnDd9deyfMWKGSMoDprXixIIM9TXR0yTmZpKuCw6IT9hLYaVnaJ6ySoyx5Jkk+OM5Aw+99m/IJ1KYdmWm8aWUuYFSSpNXEEwGOZd730v9Q2NSEiMjE3Q0z/IwNAIwXAty1Z1Imk+jp08S2NDLR0rl+GZxcjy4ktuAHSZ3LnkfLMdp4xcjlfEufltN+LRPAhhMzWVxHQUDNNEVRVk2U31YjoWk5MJWsKNZbvR5OQEZ7tPY9sOS5fXE6gIMDqW4Hvf/xeefPoZPv6xD3HLLW8rxatNaw/Y9eJzdJ856dYjFOG85sGjF+h97TV2vfAi4XCY5UvakGSFXDbDub5errr8cpAVjrx2hJGxcapqahgZGeb662+godHN5DA93o88/DDHjhxBkiR0w8Qw3CuMCy5W8Pl8OJbFitXtfOQjH3WzwUoS2WyOTDZDPqviiHpWt21CUU+CbSx6LRFCMDE6xsGdu2lsW0o+n6O+uYn7vvVdgqEQbatXlRbkzOJ58cFf8uoTO/BXVRCurKRIgRPHe5Hyx+g5cJyG5UsIRN1Y23w+7y5QxyYcDFJZXYWmuilbPv+FL/EHv/9RbrjhunI2CYB0Oo1hGkjytOdQwtAtHFuUKPVUCvkCuVyOaEUcSRZE6usIxKsIh0PUVAbx+f3k8wUqG5qJL2/lzHPPUjjfTWjpunIvnDx5jBd2biebyuIpGmQcG013GEyl8BzaS3NzCzXV7kmy56VXOPvUDhzHRk+kMLJZHATCMMEB1e9HDfjwxyt4+//5vGvyme5km/+PufcOs+us7v0/u57epjdpZqQZVatbbrIkd9mWMWAMBkyMHXJDQnKTkJvkAiGFkJ7cmJAESIDYYNNccK9yt6pVra6ZkUbS9HLmnDn97Pb+/tjnHM1Io0LMze+u51Gdvffb33e9a33X+mKaDgKZbC5HTVR1+TT1KVfFikPCQQgJ27ZIpVKk06kKG5bX46Wurg5NL4W9XWDzuqjGdaK7h+eefYpCIUM+n+fkyVOokkJtQx37DxXpPt7N4cOHue+zDxCYkuz/D351I3esW8iX//q7pLJ5ylvU7//Ob9MYETAFD2IYRZ557GEymTFGh3oRThFV00mnsgg7RzyeRPf6WLzsKgA8Xi/+YJhYNMWvfPJ23n13G7sP9FAoHGP1Sg/5TJKRgdPU1DehqCqG5VC0HMJVNay44z5mtbXjAIWCQVP7bBcSockEcnl2dh8gEKsnl7PIGya5QgEbgcfjJRKO4Pf5sEwLy7bIZLNki8aUKHjB5GSKRGKCvv5+PAKqZYmTmSzFQoGAR8EwmqdtXE899TTvvPUumqZW0NPlDbeMqG5tbWPd+mvRda1ETirIZvPkC4VphCWmaZLJZQE3ThAgEAhw10c20nvyBMPD43i8XgJ+D04+y9WZBP0/epgtmSGW3ryRSKTWzQcvHE73nebgwQOMJyZYtGgRdXV1ZDIZmpub6evrw7YtKjbmEmJ8IpFEVVSWLF2Gr7uHkdFRnnziCd595x2CwQC/+YXfoqNznou+d+zKnJBkgaq5pKu6qlH+sJDc+EuX6dvdxMfj49iWzby589A9fk6c7Cbki5K1hqdd4suhUI986ztseeZFThw9RmNdHZH6OtLJSUYGBtm56XXu++L/5LZ77j5zWDqCvVu3cyqXxj8GE4k0MSFIWza1dgFvAUSZMk3A2Pg4tbV1BAMh4uOjjI4Mk04nyaYzAPzj/3mQn/7sCT5+913cccetSJKM1+dDUWS3XYqC7dilsC25NP4OVVXVrqcUQTFv4K+t43Qixav7TrF0fguh8Tz53iS1HWHaFzaRLAosabrJ4vTJ4ySGxxk+NUbStrBMh4AmM2fRbLx+b2kTcXstMzjMydfeAFVBl11qN6kEE7KLRVcTFBCe0zrNmQKuqeT4+4fYvGMrI8k4X/6N38bwSvgD3lLIUHnzsvnuvz1I3+leMrkc8XiKXMEND/PoKtWxCDVVVdx86x2sue4mLnSNvbDGBeSKeXL5AkahQD5XoLauFq3EBJxJpcERCGExODhA5zz3Tu8gUd/WwXAmj106KYXjIBDIniCWSOGZMs3GRgY5eaILkPB5eikUCzQ1tpDPxtE0P45weG/L68yZtxSf34+m6VTVNZM3bDo6OpnVMpsbbxxHlrKoykmSE4fYu283p/vH+NKXv4waCiF0L8WCQabrPay6KI6vltYF85EVmdz4aeTx4+h1bXhi1UTqm0kNDaNqKqZlIskKllFkfHQYXdWQZQnTsnCEwON3Tz9wr9PWQBdiqA+/rqMoGjkkBgcG8edT5PODpFpqCQQ6K23PZTLkCzmE8FCmYitnKRXCTU+i6eWcWS5dWyAQwsZwf+5Mt8d5vD6XgqqQx+v3EwgEWbJ0NZ3zFpPLpenvO8HJU6eorYuwNBwiuqiDnuPdDLzwJB+56zPuFd0sks3nWLBoEYcOH8bj0envP1UKHHcwTZvuni5uMAw8Hh/hcJj77n+AwcEBtm/bxlDfKa655iqKponX62UinuCdN9/kkR/8kD/80peIRiPk84VKuJJpWfi8PnKmiaZrgEwyOUk4HMI0DGzbvUpJSPQcP8HwyAgN1ZP4vRZz2uZTNBOcGhoiny8QDp3h0TQNg+0vb2L4UBcBYGhgEE3RGO8fQNgW9tA4D/39g/giYW66Y6ObTVWCy1e2kDMnGRhzg6xzSKiArNusWjoL2zwTj9TZMZdf/WwNyWSK8fEJnnrqKRRNIx1Ik8u5bUxlsmzdtoP169cSDodYu3Y9P/vZTzhy5AgF26iE0ZlWyRQDtDTPIhQKIwGOkBCOxIF9+9nWfZwX9x9HUVRyhsnsmrfYuHEDd627EkqJC8rrvc4x0IWN7tewJvLoAS81qoYmS6yO+fFO2Rds28KSFTRNwxICHIFtW+A4CE1DlmVs28J07Gl2QMfIkzjyJqERUDIWjZEI9olBLLObgjMHf+vVlb0ESWJyMsnwYB+FQtGtqJDBMVGFIJ1MkM9kMEzjgpvWRTcuCWhuaaGpuZnDB/dhWi7Nt23ZyLKCqsoUjTxNzc3MmTuHcq/JQDaT45tf/yapySRIKrJw6cOsfBzJGwaUirHdtiw0VSYUrqJoGJhFg+RkAq9Hx3JMNF3BFwij656Ky9br9TK3Y54b52Ta6P4Qr77yEqMj44SDNjaC62+4AUWWmdM5j75Fy0DYWJkExZ73SAoPxWyWiZ5dRM1BAv4QUcWHbBg4Jc0nFq0iMZEA4VQGTkhg2jaSLKOrKoFAoKLSZpNxhg/tINi2nOqCRCqVoT8+QWpklFnLFqMnuxG5yTP9K0momkqxUEBCwuv1lbJmikoIi2mZhEIhl0YK95ps2Q66plIo5N0AdklGOLYb8uJAPmdw9NhRrr56TaUcr9fvElHEaunsXMQN11tk0mlsx6Z9zmKyuQK67qbJtmyL5GSSbDaNx+NFVuQKbb0QEjXV9YyMjGAYRTwed9MOh0OEwwuYP38+vb0nSSQmGBocoK+vj2hVjE/edx+rr1hNOBwqxaYVGRgYxjDdtDuOcO0Gsuxe2xRZJplMEwyEK4vZXWACVfcyHD9BsWCQN9O0t89CC3QwOjoyZeOS8Pp8fPzT9/Lg7i+7yHAhITsSuqri2DY522RhayurrroSx7GRZIn0+AhOfoKVq1Yg3tpF/2SWgqRQrWmEg0FS2Rzj3YfcrA+lca+KRYlFI7S3zaKqKsJ4PI6mugzb4+Nxli5dwuJF8ytexli0iv/9R1+hp6encvCcSazp0pXddNMtSJKEjYOmqugeHxvWLGWB2c+TW/fz0r4TLKqN8NUHbuKaNdcyPDRGQI1QxsdlEhPIqXHqqkMQCRJpcolBFFlxbbyOyUT/ScI1bviao6pkFdUF/QpAcpMDuvGMLj5N0TQ0XcGxTGTFJbe1cknsN7ZQdczmyNABmmwZ5+QkhYYiUqMHWqfuKDK333k3q6++Ftt0tVaPL0A2m8G2DBTFtZlftmx6louZ5KJXxWg0xqfv/Qwvv1zj3ueFUwqgdBXAhYsWsWbNtdNd6wJSRZNd8Twefy3gUlKFvCp6Jos2d8UZdVMIfIEAdQ3NTIyPo+sSpmkiIcjmCoyPjmPaMh+/9+PTypiKFWpsnoUQgvs/1+rGcpWCwcuOA8u2mBwfZmxsHN0wcEwDLRtn8PA2jm99hRXLFuDYDt3P/AxRLCJsh9mtrfzwxz9l3759ZHMZTMvCtqwK4FAC/H4/K1auqIQwDXQdRsVm1oIlJI/04M2NkpMkWtvb0DweipbFaP9JGhauQC615X/82q/zsY99AuE46LqO7tHRNa1C8eU4DrFY1OViFAKP189Va65jx/YtxMdH8Xq9blYJxyGby3LtNddS19BMY8N0frqpwEh/IIoQgkCw6py+RDgYxSKJRAJVlUmn02QzIeITcaqrqxkfi3PF6qtIJBP0njzJ0iXLz/nGnDntQDv28uWVAPAKaXBJ1q2/no7O+aiqiqZpqKqCJMsoiqt1ljNoRqORCv5NCMHAQD/FQpGh0SFs0ySVTTA41kXrrCYs06KM0C/Zjln/8Y/wziubeO2ll7CFgyeVYLiQAUfQNncOH/v856iqqy0BMAVjJ47hAPu27KSYy1MjCYRkogG2YaH4Q6RG+jlbIyi3f+6cNubOaQMBtuOG2UiSMq3/AZYtW8HSpRdeoJIkIQsZ1eNHVxT6uw6iYPO/Pn4TC+ccZ3JsDCeTIZMcwZvPoUWqKu+Onuomk0qQzmbxhSNYZhFN1dB0jaqqGorpEUb7TtC2/CpAMPfGtUQ7OgEJSXG5MFXVjTCRZBlRsu1qqoRcCocSAI6NNikoZgoo+SJd2QyD0QQBR0O29WltAbhs6YpLAk5fTC4aqyhJEg2NTdx//wPnfPy8XjEJRDGPVw9hqwrp1ASBYJCwV8ITrDkrAlOitq6Jj//Kb2EUC0iywHFcI6XjuHmQJFmhuqb2gsqjJEl4Ky746bCFk13HGOgbw+vVCckGhVwWTdeY09HB/k1Pky8Y6LqO5IsQmt2ArCrIskxtfQ03b7jpgh06tQ+G+05TEG6XVldXEQ0FkRQVj64jSYJY4zoGDu2dVrcVK5deoFXnSjAU4mOf+DS3brwT0yiW8KdyaaEKwuEImnZpcXYzAkWFhKK62tX4aJxYKIzRI+vMAAAgAElEQVQqqdh5G7/mwe/zUCgUkUybhpqZSTDKXqNyaNXZZQghWLpsMUuXLT63/CnPnF1PIQQ3r+rg5M3ref6NbbS1z2JgcIzxZIYHPtRGc7X/7HBIVF3jD77593wu8RVAoEgytnBTtfgDAapqa0sbtvt8oZDFRCEcqqI4OUyykEFRFGTJ1Wwb5yygkJ6Ac0qaUteSxlIO8zmf7/jSFqqg/eq1KLZF75OPYhSK+IJ+7vz4Pbz45JMMDk2wKG9QrJ5PY9uy8odxTAMlFKVlQTWHDh9ATOnTwaF+OtraiTWV1SGJqro6qurqpjTpbKCtW5ezl7yiqhh3rOVP/+T/cDoep6W+ga+c3k97ezX/23JmdJj8MuARl4TjOh+i9vwVEDiWREgXjGUMVE1H93i46bYbiMxddBZS2j0Zg6EwhMNnPI1nQSUqD89Uv5nqxpnJ3nHZEt6JVDOno5WBQ9spGibeWDXh2noMCwqFIrUtbazdeBeOArrv0hP3lyeqq+4LOq5YS7ZgMjg8XkqMR4mWHarmz6JQLJ5/Jl9ieW5KHzdK/xxcmTjz3EXrPYMICUKhCNetvpJHvvVtnJY2/NVRmhvrsU2bnOPnyZ9vYk17M7ptnWf5Xrj8S+nbmZ6RJWhoaOFTH/sor23ZyfXr15IrmDz/ymv0jaYxrZnZosOxGKFoKauBVJobZ88pyTXMC9tBCtUzlh1i3DBwFI+bJtoyCQaDWEjkDfPCJpiz8FEfZJlKSCUgsUPr4mVs3vEGtoBZK9fQtOcAY/veJpuYYO6GB1wblyS59qlinuZla9i8aROpjInAQUJGVixCwRCdy1ZRP6u91AWXWsPpaa4lAbak42/tpP3O23jpO//BzR/ewONPPoNpyiRRqZ5qnP8lyiXHKppmHlmSSE1mwZGIVAXdxAVCO6dBbsbIAu+8s5l0wb27SpJER/ssFi2cf/5FI1zy0cRkEtM0CHhVwuEoSOo5ZVyqTG2faRQ48f4Ogj4//nAYf00LB7dvJhbxE66qpaZ1vrs6OBdbYlmu+9bv96Pr+vQUJyVgYv+xg0SqqhlOGfScHMQwigjbRNO9KIrCyss6Eek4dXPP3wcXk7PfM02TVCrlUqsHgzOGcvwiUjmVe3oYPtVHX8akaBfQcSgaRcI1TSgo1HslZrc1E2uZ/YFOUCEEDoJsNkuxWCQYDLoaasnrfLam5tg2DoL3du5B0zSikTBHuo5TFQ1z7dVXToM2nN2mbDZHoVDE5/Pi9/vOec5xHBL9JynKHg72nKJgCDKTSRRFJhIKEAoFWdzRQiGdpKHdvVadW0aWXDZLJBpD01QkWfrAC7d8OGUmkxzftY3ZjTVEOpaRjI8yeHg/zY21RDtXIGtaCVLjED91HFvzsetAF6Yt3Ks4rlNJ11TWLO8kEI6heLzn9LEQgkKhQLFQIBgKue+WnEPnjF3p5Dp29CgHDx/hssUL6e45ga7rXL9uLR6v57wwlaJhks1m8ejaNFvxFLnwVecCvypiWbbYs2OnuOGadeKW628Su3btFbZj/cLEmRcSl/DTEnffcYu44rJO8cUvflFkc7n/X0leyySkb7/zrrj+llvF177+lyKfvyiR5X9b3R55+CFx9arl4rP3fFyMjAz/0r9vmqb4h7/5a3H3HRvEd7/9L8L+JbfbdhxRKBrir/7sS+LX771DPP3Yj4RpWcJxjBnLOpus9VIIXB3HEfF4XPzpV78q7v3oh8S//cu/ukTDF6iX4zji7bfeENddd4144LOfEsNDQ8Jxzk/W6jiOSCQmxP2fvFusWtQpfvyDh4Tj2OclXf1/VRzHEblMVvzlX/yJuOeujWLzu2+elzz3g5RhGIZ47vkXxCfu/pj4069+2SV3PvfR8+5NlxzyI3DoPbiXz6yYR2NDFcFcHOEIF0R3qR+5aBmAY3J15yw2Lmgm7feglLxpv3xl8xeTd155mv/40mf495+9QDafu+TcSP+3JX7yGNd0NLJmVSt+7ZcTDzlVBgf6KY4c589+7VZ2H+snnZqsXFN/GSJJkM8muXxuHR9dup6+QpmE4/xxlG+++RbZbLbkgXQ1X7/Px9p1a8+j/QnSyQm8Y7383oarwC9jFrLo/gDnm1kScPD9nfzefXcQHxph8NQp6urrL9wWYXH/rcuouusKTiSHz3uN/n9dDCNPTC3yZ5+7g01vv8g116w7r5nmv16Gwdsv/JwvfnQNh06cZmhomOaW5kt+/5I3LhmF9bffzs6RfgK6TTAcQJS8UKKkDosS7ig9cIpwSxsSF2OXLivBrs1BEoKxRIpoqBqfOcqa29YjFKX03P+lcE0xpRbnqathmNiKzmQyjo3ERHyCqmjsop/O5/MkEglqa2tdA+9M1zgx3Up1qdcuIQSpdArVybN8TjVSoBZZm5kCzbEdjhw5SHw8zpVXXUWxkCeXnaS2vgVFPTcn+lRJTSYZHxmguW4NP3z8VdYOj7j4ohmuZJcuDqX8KICDovuY09pI1JrA8C7Esl3ozNkpj8tt+853/oMTx0+g6XoplbJEJBJm0eJF1NTUzFAv18G0YvVqxuLD1NTWoyjq+ezrABimgVPMUluvI0e9vPHyMyxfvfrMKzO03e8P0bbgMqRUH5fPn49t5NH0C+dOdxyH03395PN52tva8Hj0D3T1/sAiQWYygZcCsYiKJhkILEBz1+h5ruHTPnEJ9S/vFQ0NMV7dspM9e/bS3NxUcjZd/P2LEsKKkkv+xFA/3cdPc+DQYbzIOLPmYWs6tgCvx1MhazWsIj/53S/S+84WspEwW996i7pICN3jq9AUTbUNgYODTD6T5MC7z7P9tecYP9GD1w9eTwhJVwgGY0iKXpkrQghy2SxjY6Ou67yEHr9ogwXkczlS43HGTg7Qs3k/23++icPv7+fk4S5aF3ac5QUTpNMZ/vLv/4G339lGbczPSzuO8dbW7RQLBZYvWzJjueXBfOWVV6mpqWHPnj1EIpGzAstdZPfTP3uY73//++zY9g61ddV4dDc0ZCai1vK3C4UCf/03f8f3vvcw27a9R33YyzM7u9m++wA+v5/29tZp7+ZyKR774Xd5+rFHUfLDbHn1cfZtfY35S1fjC0ZmqL9bv9ETJ3jvkR8SyKUJ+ELED/eQOzlAdWMDwfqGaXbAfD7HUz9+mEAwhEBgGEYli+h5bZrAqZPDPPPim0z09xCLhXhy+yDDE2l0VaE6Fp2xb1984WUSiSRerxe55LbXdQ8f+cid0/OelQ4l28mTzPTSdXI7UtJAbVLxNoGEjqKoJXjpGW8oQpAeGKHvwEHmNTYRUJuJBerxK368kSCSeq7tzTCKFM0iWqgGb7CKQEMHkqpTKBZd8OyUvioUCkxOpgCZnbv388STzxONhNixcx/tbbPRdQ3DMBEl/OB/x0bmXsEcJpKTnDzyHieOHaQxGuSVnceY1GNk8kWa6qqRpzjWhBB8/z8f5sknnqSuvo6jx47h83or83zGdSEE2YLNq1uGKMiNyFobRngxJ8b8+P1Bmmq8U/N4fe189b0Ict5dgIePdvHm5s001jbjX7CEnfv3siyRRqvOkDctgn4/iubGLnUd6WGn5KGwZy9NHpXY6R76/+EfmPfRjaz/nd/FF61xXcvymcQoAkEhl+X9Tc+zf9sh1GMTTOgqvcouOm9ezYe+/nV8De3IlQ4W/Pt3vsU3Hvwmv/rFL7Lyttu5YvYsYn4vWok6aabBdhybF77/KG8+/HM0zYNuSngDPoQjODHUiy8W4PqP3FF5Xgh47fU3eP7FVxGOw78+sQXbdhifPMHuve9z3733oigzT6oyocesWbPYv38/pmmeFWQK8bEB9u3ewsDJPvIhnb/8yjZUj48162/j/l/7jdJ19FynRCGf56c/e5xCrogkSTy0+RSKonL0xOvIsuCG69ZCaRFmMmn2bHmN4ughajwWTz/zPK+/d5Q/+sqfogfC2LZZAbFOh0XA+LEuuje9jlpTx4sv7UGxdY5teZPqqirqFi9FUqTKeDz91M/53oP/yPe+9S9UNbQQ8Ad44PO/xdVr152z+MqHtG07vPLGDuI5h3GzkYEeH6GqGgaG4lhDI8xua8EvT8/VJYRA9+gUCgX8nhCypFA0cyil+Maz528yfZLevh1UNxSZd2Uz76SHCEerGTg5TKR6hPqaRYS8bp64shjpLPFjx7lm+XWoihdvQKFagURfP1rQS2yOS/AwNVhYURS8agDZH0DE6ir9qekOtm2iKCXsk3B45rlX+d73f8wVq1cyOjGJkZ+kob6G19/azum+fuprqvEHg9xw/VU01DecEyExVcuZqvmW/27bNlu3bMYy3Tz7Hq8Xn9d1ELneeglK3u62tjaCoVD5Yzy56V2OnR5Apo3nh2qR5t/AzkO9HOzpY/GcVsLBYKV823GYM7edhoY6Nm/bTjga44knf84tN93IrRs2VBjYp409gmzW4L3dfXi8YTYfG8YTbMBMj1PMxLGJVLIdX0guclUUCGRGxuO0zm7H5w8Qq61mTvtchCoxkUjS399PbO0aGmprQAi8Pp3qphrqViykp+sk8qy5BITMgZ07eOqz93LDPZ9h5dKVLFh6hrJKQTB6uhfJMhk5fJoVBZU6MwCajJHJgqZOa8pwLseg7VBTX8XD3/gGbxw5zMJ16wmFo9yxbAltVdW0RkKU7wLljosPj7D9xddJjI0T9AWYsCyMSYuGYAzJMNj27CbW3XFrJfZQAMOjo6TTKYIlbkNVlTFMC6d0Gk7rrdKA9vT08Mwzz7haU0mz+ouvfY2Nd9zBxo0bK8/JskJVJEBrU4S+06fwaDqOleeV5x+j++hBNtx2B7Pb5jB/0WVnJhxgJwdY2t7A/hMjpFIpikU3/EfXZJzMGJZRRCvlKCvk82zfvgNJ8xGpqqWqxiRdKNIcdnjkm3/O5GSa2+75PMtXXVFZjBJuJKmDQdHnpdC2mIZrlxDZ9QYHRvtIDgxBibwTYGRkmIe/+x+MpguY4ynykge/J8mff/XLfPSeT3HjzbfQ0dF5BoAqBEKyGTvYRWzPQSKah6gko+saQnEYHB2iSfeSP92Hb850l70kSRV2GtMqosgqtm1hFIvn5heTBJPZY4wluymatbTMDvGRj9egeyVkvIzGB5gYswjNsgC9POUx8wV6u3vRNIX5nUvdDUoFx05hJEawrXpkVZ12A5QkmWLRwOvROXToCOFImFmzmpAlpQI2BshksmzfvhOP5mPHjvfJFPJIjsHRoycQKPR0dSNhc+eHP8r8ee001DfQdaSHV156kd7jXaiyStEsuhmUHYna+joCgQCFYpF58xZy+4c2IsmCb//zNzFMNyJB0zW8Hp+LYXMcFxIiy2iqymfuu5f1N9xQHhYMyYOjBFi+pI7Llqxg/6ETbDm0FyMQxjDNad2bzWapq61j56mdHD1ymP6+Pvbu3cfrm15j39593H//Z5lVSjXkdpIL76gJOdy5cJzXe0OMpRO0RyO0NORY3Rm45Lx1l2TjCvj9bN22g7qGBmbPmo3P78MQbhqMkeFhN27PcRfynDlz+cM//EMSyQmOHDnAwOAwIwEf48PDSJkMxWKKnhPvowWgfc4ShKtGMXz6OA3VMeq9QVJmBs0qQlAlPTmOUywilxDRAD5ZYt7lV9LS2ECm7zTJ+DjHX3iat8eSvChJVFdV01hfy/33fZYNl69ElhVy2Sx73tlK36FuCmYRx7YIaCFM2+R0fgDJEZx6/zBmwUAJae6kFIIrFraycM4s+keT7uKR4PrVS7lrw5qKDaZyAtk2Dz30EE88/iSjo+OYpkFVJIBhg6Z52Lv3fcbHx7n77rsJBAJEY3Wsv/3T9Bw9TDr3PJiTxCJhhseSDPaf5HvfeZBAIMDqq6/jgV//bXylrKzx090smdtO13CaRGLC9bKoGkuWLOPuG1djZOJoXpcuLhyJ8pFPPcD+fXs4tON1AiLOuiXNjHW9xa7DY5zoGyNRUKirraahpa0U9S9wAEmRUfIZAvFeIv0aQU0QUXSMYnYaFi0QCHDl2vVs27aNoYF+RoaHEbaDR5X56cP/yUvPPE1dQwP5fIF/+td/o6GhAQeVcGOMZcuqObr5KLV6HSJog2WRU7Is2XgZavjcq4YQbkocEBSMvHt1kdz0xGWUfvlZN04whV2EeHGE6rpmJCtANp1H1SxsChTlIxSLc/F4ymhwgRbUmb+wFisXcG24koKsa0RqHGpaVDdB3tkQAnDxXqZEW3s7RdOgUCiQz+WpqTnDPOX1emlsamTP3sOAhCwEjiNhmRammccw8ghhc+xYN8qd7oZyYN8R3nv7GF6fB8uyMMw8voBDvmiRGBN4PD4cxyIxbLP++usIhv04jgsAtopFilkLu2gQCASxLLuyjgpWBruSNFPCctxvRoSE5okwPpmmqa2O4u4cvmhdiSPyjObr8/nweDzs2rWbeDxOZ2cH4XCIve8f4K1332XHjvf4yle+zJVXXjHlUHGwckmyIoihNRNtaiavgOLJITt5yjyOF5OLblym6aYJ7pw7l8Pdx5jdNougz4uFRDabJRoMY1YslgJVVamqqiVWVUNLSzsjIwNY5iS9J07g2AJH2BTyOU729tDWuhBZ1nAEhCNVjAwmoZhz7QDCjS3TvDqmUTgDrEQi6vPzP264joHxcSYnU+zdv4/BvQe4cn09hu1QlEDRvOwaneBmIZCB090n2Pbca2SMLLWBGtc2LFR02UfYE2MyM4Hm9ZFPZvCFAqWyoL0hRCabRZZlN2ZOElQFVNatnEeZYKA8eU3DZHRwmORkCllWqKtrxHGKVIcUTKFjThr8+7e/w+JFi1m5aiWKqrF85VUsumwlCy9bzqsvP82u97YxmczhCEE4HGV2+3z8/sC0lEG+QJCirBEfG6vw4gkhaGhuQlNU8pMJAjUtSJJLRTZv3gLmzVvA0mXLeezR/0Sxs6TzKQwlT+vcEEYxwzf+6R/56tf+lkgJqClbNlUxnZZZ9eSMAv2jgvCRflpnt7DqzstdDF9pEodCYf7iL77OwMAA+99/n+eff47DB/YjOTayBIVCgb7BYT7xqU8TDkeQEMhIWNYknloLJegj7ahIksCQLGxfFLUa/N5QpYypYlt2ResSsqiMTTI5STB4JsjasW1GD/QzeGychisCmFYekSsiJBvHtpnoczCH9tO08SY8nkClLM1M4dN8EApj2y5/qJ0qkrcsbPt8/k5BfHyCcDTqxvIqckkbLk7buCzb4siRI+71XAhsHAIeH2rQw6nBQWzHQpZkBgYHyGZzACiKiqrqgIokOQhcbkhV1lAJokg6suSgaTpqKa9XcjKJYdkl04mEIwT5fKGkKICRz2HZU5Hwgkw2T7FoUlcVQgtGMWyFdCbBXdesY/vIBJx1hdNUlY65c/jnB/+JI0eP8dPHHiOVStPU2IhH99Lf18cjj/6IZcuWEQj4S6VIZONxetO1yEoQJAtTdhjOqxQm44RijVOSIZ5fLrpxGaZJdVU1sqRw8NhhikWD/v4BUFXS6TR9fX3kcobbAeX5VVIJdV1n1qw2QNDWvhTDMMjn8+i6jsejIytqKbRM0L5kNV2vPoeugW5qKI6DmssRMhU83tAZL1Tp2wJBc00NzTU1LJjTzj0f/gjFYpETvb0kJyepqalhTmurG/OGxPxll7Hy2mvYsWUrI8UkRbNIRAmTthI0yg3YMvhQCTdUnfFhOiZbt+8ikStSNEzUkjaStlUEGo4sKgzMkiTh9XlZf8P1PPvCSxiGweDQALrsMIaMadkE/AE2bryNpcuWVuw+Qgh0XWfx0pXMX7SE7u4uDh86RC6fo6mxiRUrVxGNTs11JqF7vIS8KtlsGll2vZXCcbhu3Ro0PYW/+tw4RRDMm7+IL/3Z32LbFl3HjnLs2DH8fj/t7e20z+mYYiOScGSYGO6jaNmk8jm0nr0I00TxRqhtn+eO25TvCyFobm6mubmZWzZsmEJSe0Y100pZBiilhcnnC5xKp3nBSCIFbCQhsGwTvyqztFgkOCWH1dS2fOjOO5g3vxPTtEr1kKivryMUOots2DA4sXUfZsBPeqKWiTGJpibXHuUImN2wkuN7fo6VyUPpVYHAKFoU8ja6jgsglQSW4WBkC6BNL6NSlmUjkEgmksiShKJKjI4MlXLhnxFFVqirreXY0ZMYRoFisUDCcRMN2MLGkTwIYdDe2kxTkwu/mNPRypKV813NWpexbBvDTCEcFa8eRJY0DLNAdU0Ur8+DrEisW38j6UzGTUNeHoLS2lEUBWwDRffQMAXikTXyKLrOiOMQH4hTZ5ukJPAEFXTdO42ha+q4qKrKZYsX8ad//McMDQ2xc+cuisUiLS3NrFixHL//DNeCI2RsGwYmPdiSgSNssMCJNJHOTxAQ8iWRn1144xLurprJZdmzdx+J8QS5XJZ8Lo+EhKyoLFiwgP3797GgvZmzd2S3bS7iVpJcXjhXJT/XXa35wnja22m/bgV9+/oJh2MIU0B9E6asu/EF08Iozmg7iiShAJrfz9JFi6eFwZQ7WJYkbvrMXUQ7mtm9dTvOeJqsYwIOwWiMD33qY9TVN5TgAaVOtg3mtLcSCFdTnEiQzReIVlURrWkqpV/hTAgJrtZz9TVX87W/+DNeeeVVHEegKWDZwiWgbWzmC1/4DTcucorNpiyaprNw4WIWLlxcuRbNBKEoWBrD/QNEI27mBAEuISgOgWAQb7jqnHfKY1D2wi5Zupwl5wvyLY1b9axW9vsbOTpykuLwGKFImOholsvGs1wmn7uhlKWcmmfq/093m5fGRFUZS+UZTxfx5RwMYZJzikR9CkZOoCgzc3rec88nXG1rCjfA2UHc4J7uE4kst374IyS0I3h8kM3mEZKNbUlU+0LEU/mK3adct3gqwbsH9lAwdbyySjafxatI3H7TPORI7Tn1EcIhEx9hIj5OfX0Duq4hyVBdVY1HBcexkGWt0jef/eynsR0bo1hAU1Usy0SSZGRNI5PJIBB8aOPttLbOBmDJioUsWNwxvT/PFF5CywskSUb3uOX8ry/9wTSoTyXC6Uz3IyGdYfyRQBE2jlUkY7iks1lkHMekfjLBJ1fPx69dOFZN1zVaW2dX6n2OlOruGJMc3PIoI2MJkskEkWg1Aa/Gp6/6/Wn2wAvJxTcuTUNIEqOJCSI1DRiGwCMrJLI5cgWDgaERFlYFKaZSqLELY5vOCRsQVLA0lrAYKwQxI9XYK3zsI8zcBXNRa6uRPf6z98QLFHJmIzn7B/5wkLU338Dam2+YoV4zt78+GCDsVxgaKiBsG033kRs9TXF8EE9s7jmvyLLEhg23sGHDLedp/0WqP8OGNr0VUDd/GeN4qK+vIpvNoSgKkUiUOXURcqcOXhAreEmudeECiyOhINdfu5CdR45yzdo1dHUfZzSVYLC3iyXruCA+aSZN6cz3XRr4cDhGJFLL0Ogu8kIlk0oSjMb4xIfXEAjVnPebkgSyfHHzrCTLqNEweTWBpAocCkiqhCy5GWNVv8mCq29E0aRpmK5QpIru4V5e3LGXjRtuJxIN8uzzj7NqxWcJzbCZCsfE7jvMu2/s42jfCJlkgmhdI7Gwn8/duIhJxyY2u7MCc1m0oIN//Luyp19Mu01MpWqrbPCyjMf7iwGez/awXkyEEDTqDhHVJJ5zcCQJSwicYpYVrRpzYyFUf/C8718Sdqv0ZyAY5qrFzfz4hR462ps4PTgKtkxhIo4z20JRL54k4MKjL4HtCKS8QV0+y/GhCR7dcRBNk8lm80RiVTTPaqNO9ZKfnCAQi3LB2TxDQ2zJtRlIEiSFTa6qkWIAYgUTze+jJlqFoqmc8WHN8B1R/tqZek/Vgi5aj/OA6GRvgOi8y/nV++7jX//9ITy6hi9cRVXLHKS6Tlwg5ZnQgfJku5Qy/8tSchDoHg8NDU2sWLGCbdu24fP5qGluI9jW9oER22U4iafpMhZcaTP31V2Ewn5isQjCCeJpWICQ5P96GZKDQEIP1dK+/FouWzHEcDyLxymSEbB7MMvCa5povgTyUQkJIYnK3yv/L7n4rva58zi2d5j9J4+TzqTx+bz4Al78/gALOqPMU21yIyPE6sreL4EvUscdd32SgwPjzJ07l+vX30BXTxfffXUvX1w6yKyW6RqFJOuMK1HeP3qc8UyBBW319I2MMTEuOL5qPldH66Yt7OmLXJo2WJJ0cY3jnPklnR+cLab8XinvnL+5fxfBEJcv6uDgjx4DVeFT99zDQN8A7+x8k6SlsbF+IYr3g9H2yQi8TfO494HPseVAD031tdTUN5BOp/DPWoA8AwnwTHLBIGtR0kOFcA2UhmXy6KM/wsjk2HjnncxunY2QXApKpZyL/RcAy4npv52TzRNKG0iJQOB8wZpCCN568SlUrZprblyLosxMVPqLiCSVUuMI11s4MjLCs889QzqT4Td/4wsEA4Ept4tzvV/ZXI7nX3yeTCrNZ+79zLSc4x9Uyh6z8lWynGhPnYKC/6BFVTBDuPaXszFLHwg5Py1awA2sF0K45SiKayM6Txnl8R4dHeGRH/2ApUuWc9ONN88I1BRClBjKBbZjs/XN1zi0fx8f/eSvUFNXX0pX45bhgh6nHzqWZSHLMrIiY5cyT5TLOXt+lcckn8vxyEMPEa2Kcdfdn0D3eipl/LIkX8jz6I9+SHtrO9dff6ObufU8W5coX2sqzZKm/DH1LVFZimUSE1V104nbJaJb5QOCYctXWvcP1zssStdZRZYrYXRTyjhvYRfJgOqeBpIkISkqLz72E+J7NtMQC9G7fxezZ7eiVVDr56mscBmBLcsiFApNP3mm/3ZRnr+Zvg1w5FA3YvA0o5P92GuvRvFdWj6qi0nZCaAoCkcO7qX/8DZGR+N0HV3HylVXXnB32L5jM29veo7agIeTvVcxf8HiS2rP6OgokiRTW+telWaaKK42cWboftF+uxQpg2QlBOnxOJPxCXRdp3Fu+5srZq0AACAASURBVAdfhFM0BNc5I/H+3t3sem87165bz4KFl1VgDjOJEPDP//R3iOwg//Las7S2tjG/lDb87DZIiuu9Kxbz9B/cxuD77/GsDJ//vT9Cks+NTij/23EcFMWN6BDCIRCuxrUTzvx8eTN74YmfcOrwe3QVMiyYP4+VV1z1Sz2wAJ786Y/p3voyx3frxKIxVq66/PxLXAj6R0bpGximqbGelsY6N/ewkCp24/J3n332RaKxCOvWrqG808my/IEzjpTFXepSSRGy+M+HHub06T5yuSw33ngjH7pj4yWXdem8irJMS0srUttcGmMBaqqCSIo8BXcxszaUz+crGK9UKkWkTIc15RnhOIwODlDf3OIm5f8FRDg2h/e8T2FoENMUZDIFqny6C6b8QBemM2LbDvHhfj7/0evZsmMnu9/bzMpVV1YAm2eL4wh2vPMan96wmnDIw969u5g3f+FFrwHx+ASJiSQTiQmCwcB5ma//O0QIQWoiQc/7B1i4agVIEoqssPv511i49koCscjFP3KJYhoG7779Grt37SWVSjKrtb2Ub33m8ZMluP+THyM8eYTBVJa6apekVpQ0g5nGxOP1ccP1a1mzsJbosts54w+eue259DjbX3kIx5ZxBCiSxbUf+nV8gSrOvt6V3wG4/eZr+cTaDlKT42jts2fcUGaKJ71UEQhWXzaXm1pup6CoxNpnn/PzsnYlhGDvkS4ee/k1UoZNKOSjuaqKj990HY21McrGF4EDjsDnnMZJy/zwe7uZzCt84Qu/cd7Qs/OVeal2XNt26O4+wXg8jnBs3tu5h1tuvumS5/wl7RJCCGwhaJk/h96x06SySVIoDI+NYhiFyjNT1exymEI+nycQCBAIBHAcp0JDNVWGTp/iK//zd3n3rXdITCTIZjIU8oULXvPKP8uODtCopKmvibFq+XKe+8luhvsncbvzl2NrEoZJJFZFxsjTNqcZPRjDsUserSlFCEQlzObokUNQnCAd72fPzm0c6eouASfP3x7Lsmhrb2N4eJhMJnPeSVDu20KhSDqVIZmYZGI8QXx8gslkCsu0PpidrfTqib0HqG1owBcOEauvI1xbzZwrljPS2+uq+GeN+S9UxJR3i4bBWDyFI6kMj0yU4vjOf81PpiaR/FEO9vYhOzmee+Yp+k6fdvk2z2qGEAJhGtipEWRZJxiNQCGJkUvCFKahs6XrvZcQRo7rP/Y73PzJ3yeTnuDwey/h2NNnVXnMTcNifGCc/p4ix/cdZfj4OJMJk3w6P2M7xseGmExO4Dj2xfux1NeOWSQ3OkBULpCdHCUbH0bKJDFymbPedzeSomnxyradDGWyeGSZxESCnYe7OHj8BI6QKk4B96oMne11NFR5sYsJfvbYz+nu7pkyVjNWC8dxyE6copjqI5GcpGhYF22Pa+KQkGWXmzUcibBgfgeafuk3pQsGWQvEn4PEZCrFT594nL/553+le2CUeK7Az97exmNPP83A8DAdHZ2ESqzIkiQRj8d58cWX6e7uYmBgiHnzXFYbj8eDYRiVxGTlxr371mb2HTzMPz34IH29pzm8/xCJiSQts2ehez3nvb07psFkzy6efDFP0oghKQrbdpxG8/pZcFnjuSf2FKv1TKfe1P8r18+ybfa8tpvo7Faa2udS174CpbqZeGKSaDhSsqed+X4ul+NLf/LHvLf/EEU1yHBW4rlN7/DTxx+nY24HnXM7ZrTFAPzoRz/hyitX097exmuvvc7ChQtnfHbfngO88uwbvPn6Ft545W1ef3UzLz+/iVdfeJ2tb++mtr6appb6SlsuNInO/rlr24Ph4yd5/uFHCUbCzF4wr/Lzo+/t4tnv/Ce19XVogQBev69ir7As0yXzsCwsy3Rp3KzpvyRZmnYdGBkZ5p+/8Q22bdtOU1MLyVSKnq4u5s9fcI5pQQhBb28vX/+rv+KnT/yc945N8M7hUQ73HOell1+kKhZlXueUJI3CPbyMgb3k8xnynibkcBMpw4uaOIDu9SLp4XNR8I5F78E9zFt+BbLqxc6nqatvYTI1SU1zZyUGrzzmDjbPffclHv3rx9n59j66+nS2bh5h00/eYqDrNEuvXYqqu9EYAoFtm/zln/whLz/7JMGQn5raWlRVo5wZ+JwxxwVu97/wAw4+9Df0791JITNBurePEy89yWTfIeqWX4uqTfc8yqrLVXiouwtLOARDIbyah3UrltJQFa2YgVxAapbUaBeyZNPYWIskTMYHeulcuKgEzp0hkacQbNuzm+2v/DubNz3G2+9PcOT0MA11ISIlTN1MLO9AKZ5yCx0dndi2Q2dnBwvmzzu7/V87901XLhqriICHfvRjHn/hOQqWBQJ2DsSJhaMIIdN1vJfte/fw0Vs3VIBjRaNId9cxDu4/QDhaTdijs3DpEnKGiWWaVMVixGLRShm9x4+zeOEi/D4fxXye3bt2EZ9I0tDcxFVrrpg20acOamqkj8RkFsMSxPtN5rUKrl4V5qprO8q1n9LHZzxPwnHOsrOUn5yC1Cv903EEB70wtusgV8ypIzw+yZbjg5hBD413B6mtb5jWYyNjY2zeugXLtHh9897Sp1RymSSbNm3i9g23Tu9hIUgkkrzxxhvs2b2bJ56owrFtDh06hK7rrF17LdHomfxX3d3Heen5Nzi6rxtZkdD9KlXVMQr5PMKWiecm6Ok6wcrVS5BLNGc9PT1s3bq1wk84tWzHcfB4PFx++eV0dnZWfr733a1seuopju3fR3NLC7OWLmSk6zhPfO3vONXby/Lr19J55SoAEvE43/7GP6IqEpZlUiwabq6s0tHhEoFoWI7Dxo9+glVXXQO46XZ+8uiP2Lr5XbK5AkWjyED/AGOjYxzYv5+m5qZzruMnT5+mb2SEFWvX0dnukpCMjo2xc+u7TCQmSrapM1dygYTIJdnXW0SPqaiyzMjwaS6rzhOpdzh7VQnhsO2Nh1CKO9j8/NvoaOTio9RGAphhOBV06Fx1z5nwFyA1nmHvGwcpmgaz2pu58rbL2ffmPnoPHWP/tv1sfvINbrp/Y6UMWYb1N9zI+3v3svXtN9F1Ba/XR13jbBoaZiNPYeMut922Coz37KWo6LxPFaOTMtGiQovq4O3ah1MsgL8MjnU3CxmJ+e0ttDU1MpTKosoSKxe2s2hO69SgDwCGh3owbQNVUdBluOWmKzEMh/f3bWXJ8msIBasrc6OMzxQIJCXCvuwaUiNvUWv9FDNtMDbvb2lpaEBCBklGOquw8mFp2w6XX76S5154icbGhmntvZhclFcR4MCBgwye7icUDOENBxGS62kqFgr09vby8iuv8JFbN1BWaRobGrltw6201jdx7PBBBvtOk0ilUPQguiphdHS4G1dpt+9cuICD7+93WW50CUmWmUwmePbJp+k/3cflV64iVhUjGApWUqWARLCmCUvy4/Vvo2jk2bkvx933rKSmMXTGcF5qRHJ8mO2vPl5xJmi6jm072LaFIssoqoque5i7/FrqmttLr7qLZlFDK35HxhOSUByb64KzyUZiqJ7prlsJiaeeeYqJ+ASyIhMKu6esZZv4vToL5s2r9FFZ8vk8f/s3f8emTa8hSRKHDx8ln8+h6x62bXuPp37+DN/93ncqi3H3rr3EE6NIuiCXy5E1HBKJCRRJRZIUHGHw/AvP8qG7NlQIeh955If84Ac/xHEccrk8Xq+nlK3CBa7Ksszatev49rf+jWAohGPbxHtPMpFPoXcf51uf/21mtbQg0hnUsTH8isy+N97iunvuRlFVNyYwEUfXVDf4Vwgsy0GRy15Om2LRQJUVsplUpe1Hjhxm+47tmLbN1ddcTT6fY9bsWRzvOc6DDz5Ix7xO5syZU/HkOUB1SGNxnZeJoQEKsxro7eul98gx1s0JceOCIJI4QzSMJIFtIbQQtU0NTOZd76Wm6ZiyhpUaRq9uphJgXRrF4QmNUzu66WhtxLEkCsJiIJ2mypapnx1zSXOniEcu8JnPX8bzTx9g/uXzsbGQNJmAP8ONH+6gs8M7bdzTk2nCPg8PfO7XGB8fo7aujnRmkkwqTr9lUFPTjM8fpBzxAGBMToKuIwyD1YEJCgZ4PBoioIHkB9uqzMGpm4Tfo9HZNptTO/ZSNIo0Xr0aRwJFSNOcS4bjQY/NZ+DUUZobIojMOKokI8tF3n7hEbKWyrXrb6OxaRaq6nrIJSSaqv2M9PWywD/ATUtNnt3fxoPfe4lZr+ylrrqW3/zcp/F5vZx9QICb88zn9eLzeVm2dMk5P7+QXNirKFwOQVsS4Agsw8SxbYQikS7mUYBcJks6ncE0LVTtDEOzUSiQlTy0zJpNJjGJYzssvHwB8ZEx+o51sbCzvbKp3HL7rVy9dg3Hu7rZ9OLLdHf1kE5n6M2kSCYneOv114lEo3z6s/eydPkZVhzV68cbckimxxiPJ0EoHDqZZtG1U3BcpUIs02AyPowqy66L33bcfE6KjCiFp6iaTiGXPdN+CTLJLPbgEA1VATJBhaJp44v4COheZMdmKqJfILhu3Voe/t53KLjBkDiOe11ZvaSTX7337ukDZxgkEkn27NmLoig01EeZ096G3+/Dtm32HzzCkaNH2L//ACtWLAcEixYtJJlIYuaPI0sqhmGgqQqmIfB5NRw8rLrmMiYmkpWNK5vJVhh3QiGVqlgE0zRJZ3J4vR4s00LX9UpSwWIux0h3Nx2+KKokIaWyDB8+hq3LICQkAb17DmAUCnj9PkzLYmB4HF8giD8QZDKVxuvRsM0spmkT8Ov/H3vvGWfnVd37f/fTTj9nznTNjDTSaKTRqFmyZNmy5d5tDLiFFkoIJEBI8icJSbg3CZCQhIRAyg1wE7gEMAZMszE2Lsi2jC0XWZZlWb1P7zNnTj9P2fu+eM6ZrmIwuS/+rM9HM5qZ8zy7PM9ee+21fmv9iFXXYBoWjYuXTgVkhgb6mZgYx9ANPNelu7ubRYuaiEbDuI7HC889x7Kly6Y8sRqwprWW9956EQ/ss9mz80WKxSK5TJrWxRuoidfMAboq3PwYupBEkzW4WgHXLZFIVhOMQy6fxlSzHQpCCDa2NeIeiGAXIY8g1rSCif6TFNwoQyezJGYUQlVAUCuxuEaQSLYwMmJDMcdFl67nFS/FVTdvxQonqdifCkW8qpqNW68jNe6zYEvHIR5Pkk5P8r1772F4aJQ7734HF23dVq4XBpap0di2jNTJY8R1SIQFrmdj1DZR03ERhimn+qNQCCUpOh4P7djJ9l0v45RsktVxfrB9Byd6+3jzldtoqEpMvbvt7atxHId4cgmp1DDRmjjHjx1GeDkS9S1U6TrP7XyMNWs3sHbdtqnx19Yk+ZMP3Irt3MQ99z9Pz3g/dv+TpPpiJOuXMHTbTbQuaYIZlnPFZ1Z0PIZGRhgeGn7dkctzRhVPnDpFV08PDYsamRyfwHEcQEeicGyPZFUVExMTHDlyjA1rV09dF03EUYUMiViU/u4eRoeGaFjcDtEYY4dPznpRAGKxGBs2XciSpa3s2f0yvd09PPGz7YxPjLJ5y8WsXreOuvq6WdcppbAdmwee+CI9PacBwe7jK7jqxkdoaJgJ+vMnTRMGQtPwXAeJQBgmxUIBvZK+os1OsZEKjh84Tv9QL/EVm5lIZ+i2BUJYbAqECQbMuQYUy1tqWbm0iT0nelGFgn9c8zxuu+XmMiHo9IdN00TXNQYGBpBSMjIqGRmZ8LuiCXL5AsWiy8EDB8uKS7CsrZXJyTSvvXIYx3ZwHMcvVicClIoenihw8SVbyGQyU+3ccuutdKxaNfVzfX0t0pMMDY8g8MvrrOxYWUZbK+x8nt49+wkbFjqQKpTTYhyNoBVA03RkySYzNEy8OokQAtMKEQuFCAQDxKuW4LkOo8MjBIImVXU+ZspzbOyCH8zJF/Lcc8+3yGbzrF9/AUODQ3iux+TkJMFgiFRxknvv/TaXXratTDaMP9euw65X9vPSq3nQPFAS6br0dx1Fu2KLfw6bIUYowdhkBjviM3DrukkgGCGWDBLyLJiHwFcEdYux8SK1AZNk1EFIQXxZCycODTAxlmGueLrJ0UO9dB2cZHj4EOGoxcl9B5DZUQJ6HGFO++oqajISjRGKRHEdm1NHD9LW2MqJ490M9A/T2bGCWCSEXbIJhf3+FYslnGIepQTS9blDNSST/SeJda7GKxYwE/gKAr8c1Q8fe5wHnnyaeFWMD7/jTjwh+M/7HmD3waM4JZsP3vUWzBnYLMuyqK5poLq6nlRqjIaWThy7xODgIIVCjrVrF1OVnM5oEEIQjURYv26tT2DsuSjPwTLeSk/fAJs3Xcjiltl5szPneXXnKhLxOE3Nza8bF3jOlJ/h4WFGR0eZTKWxTBPN8xBaeacxTd72tt/gO/fdx+DgIMxQXK3LlvLMk0/R3dVHMZvh4muuJRpJogUsLv6Nu8oDn26q0unqmhquu/EGpJTc/c63T02oOcOag+ko3Bf++V/p7u7CcR10TWdsbKysXGeKoKp2Edve/Fs4jg96E5pf10kT2lRZZSsQIFk3vZ0KCXW1AT7/le+z/3/9I8ViAcM0ECi++LnP0Lq8mqns3LI4uTzHhtK+ZSolUtqEQkEcz52H/hdCYBoG1dW1DAwMMDGRJxj0E5FLJQfHcUkmE2y99JKpa2KxKG3LWjl+8ijCM6cKFCpUGSwJR48d5y1vuW2qjWuuuYZrrpmd5rSQ+JuBxPVc0rksk66Ni8LUdYqOjS5NVMnG1DSqgmGGu3po7uwgUVXF+z/0e0TCMd/vZpnlxG/l+zjKL6VULsuWtfpwg3yBnz72OJ7n0dXTi1MuAxOJRDAMk3Q6gxCCnzz8EH/4B38w1UfPkwxnNVzpoSERSmI7Dm4pTcF2sOYdSXRKhRwvH9rFyNg4JbuApgkSlyyjddnSBQ4wAicYZWDcI9BgUp8IkZkQHDw8zHgKQrH5loGmPKyGVQz3PYxEkZ+wyY87JKoERbtAvLZzwfnWhMCyAnSs9UlSN26+hHUbNmMYxhQYs+KuCYRjKCOIblgobJ/tSHmYZpBgKIgZn1YoPtBTsvmCdezad4DmRbWsWdqKJ2BFSwO7Dx7hlVKR0fErWFRbM8uPKIR/hExW+0aClJJ41SKUkhiGvmCqVSWVaetFm6Z+t3nzuf1VsVgUz/PYdtnWKUPkjfFxCYXjuuhC0NLSwvDIsI+V8XxwoBQSy9Cx7RJmYDZrbTAY5K133zVVmVI3TZyCg6brhKPBM3aw8ntd12eVKFkwpDw6zj3f/DZ1dfX8/u//Ln/xF5/ijjtvp7GxYdY1CjBMk6bWZfMspKnJmps2BChdEA9Mcvvlq9l/7BTFQpamZAsl1+WZx+7nyk1r5/Ye082wbv0aBiZyuIUCAokVDlIfUFN+iJlSlUzyrXu/TrFYgX+oMvATEH4SbFvbsllm9qLmRr78lX9Deh5TmKKy8hLCr5QQiU77FV4fXkhQVV/PJ392P1KVd0HlU4hVXmqhQNc0apsWAYJIJMwVV102Y+JmfZt99/ILWlVVxY4dT1Eq2YyOjpJKTVBdXU08HicQCEz5tWpnkM4qIXA1g3A0ykDqFNlMBoTiwuXN3HnDdUQaV89vUIOhrMvXvvTP9A5PIIR/RI+n3sLdv/O7xNVcHLGiYfkaPvLZrxEOC0xDp1Tw2Fxy8TSDuobZRCECEFKQSvdQe1MHp4+dxssWiAQjxKoVws6ghOR8xC9CMI0en1VZIxbDCCQ4NpIngEQEPDxXggE1Y+PM9OtVbLuOxc381Uc+gGHohEJBlFK86cptSNth49rVVCfic7swT4QQs4yGs71Lr+c9Gx4ZRdd0du9+meXt7a9LacF5OOeV51GfqGbZihU8Mz6OKpfvQGkYlomh6dRVVRMwZt9K13UaGssRN+HfLVQuRfyL4EIXGtR3vvNdxscnePd73s4NN17P//yLTxKPRWehyqeaX/CHGfcV8/8ogHB9O5dtyNDa+ByWBddfey2P73ia/pFxlJx7hSIQb+BNV1/OcGQJ8YDBhe2tPHnwNHl3kJLtEArN1py6rtPauuR1gRL9go1L3zBE9lzRDZMllaOlmO0DmlpMU0ef6a9TIljot9N/Li+Gzs7Oeb8/K3QDhSSIJx3aayPsyWaxdINQyGQkWyAZmFPHSQgEOjKxhKLjkowHMa0ASnrs6Z/kt5tWztvEQBCwwjQubpsaQagKUOW+iXmaDhWIc2y4QGNrFQdPZMkZLh3rlnBBWxLdCKEb506QPlOGxHS/dHJ6jFNmjJ3HMwylRjDDUa5a186tyzeiVMU5X/5avl1NVWLW/VYvb2NNexsoDyHOzdXwq3jHKieNyy+7lGSyioHBwdfdzllzFaVSqqenh5d37yEcDjGZyTExPopmWiSqqggFAtQk4mTzea679tpyvuJs60gpRbFYwrLMcmTsFyN2nStKKXbseJpDh45w9913kEln+O5993HjjTdy4YUb5zz0+VIoFnFsh3i8YtXNUVpTi0ghJTz48MOU7BItTc109fVTW1PFDVdfM2UZVdpSSjE8NkbfRJpEJMyS+lpeO9VDLBanpSZO2DTnvfgzx+Q4NkXbxtRNv1TNr0g5nY9Uxp/PF/A8l2g09rp9EefRCBJwXb9aaCgYRTN1dOEv1pnKrJw4y7HeXvbu24eBIBaPk8rksQyN2669ZlZZlMq1Skp6j72GhiReXYsspDFrFhOOzMdwzbpOKfK5LGjCL328EIpcKVwpOd0zQDqdRknFkaOnaKytYuO6dqqqa2GB1KLXN0W+NT2RmuT5vXvJpdJYukYqn6cqHOS2m25ENxd+V/x3yiGfL2CaBsFQEE34ASohpn1u05+XoErYriJfcgjoJqYmMAJWeSN4Y9J/5mEHF964zzxplQd0hn9TIj1XTYwPq69+9s/U5/7891Q2ky6TcHrziDhnEnS+8MIu9a53vkd98Yv/W5VK9lmJVOeSe54P0afjOGr37t3qphuvU7fefJPKZrPnbMN1XfWnf/oxdcdbr1WvvLLrrESf/50iPU89/tD96h8/+VH1ub/7pCpWiGf/H5HPep6rTpw4oW6/40518403qGefefYNJ8KVUipXSvXY/d9UX/rkR9R9X/2icqWnpHSUewbS1wpJ78x/5/vOvJ5+HTqwX915643qTz7yPnXq5Ilz3tNxHHXf97+rrr72cvXpT/8P5XnuGzpfUkmVzWbVp//qL9Wdt1yrHv3pwz6R6jne9wd+9EO17ZKL1Id++31qfHz8rJ/3pFQTubz61F/+pbr1msvVn3/8z1S+UCyvc+eMbbyRcz9Dzqibzlt9KqHx6AMPcvW65WxbWcuOxx+hXDlonlqs7MqObfPQPV/lbeubsfpe4+Del8+qQLtOd7Hr+Rd4Yedz7HrhBXa/uItXdu+hWCic8TpN11jRWMW/fOw9fPw3b8QSRcSC3pVpmUyNsW1lnI+9dQteeoDzq3L9q5ee3m7c8dO889ZtROQ4fb1dZd/V/xsRAoq5NLdsbOYDN2/gxMEzP79fVPxggEe1rvGuazczdPoIFe/YuZ7KqZOnGRocoVg8cyrVLyqeUkwMdvPemy5ga3s1mrLP2iGlFNlMhr7Xnuczv/tWWvRR9u156Q3vVy6dIjh+lE+87xZee/7JMp7q7DKZGuXP3vMmbr9iLaU5aVELiZubpHjiAL95xQYavDFyYyPlNXJmdeG5Lo5dIp+ZxHXsXy7l7DzkvJOsi7k0wyPjaHUGGiVSwwNI10UzDBZKZ1ZKoRsGt996E/R3EUvWsHr16rM+/J8+9BCPPfQwpmnhv7wawWCIz37hH2lZfAZiTSWwhU4sYCDqq5jIFKkLnf1hxuIJVi1bgunZsGgZrmdjnYcf4lclqqyccrksx0+dZNtFK9l38BBX2qW5sYRfqg1PevQNDFIslhBIli5pncX5N/8ijbblyzEu24pZGCa44jKy+SKRkD9Xb0TVAIG/kQz2n2ZxuJaslPQPDbCovpEzML8BkE5nSE2kiIQjHDl0lAs2rntDj7C6EFy45RK67dMYkQSNjY3lRJIzO5HzxQJ7XzvEO27ezH0Pb2f5Vp+Vu1Jk4I2QydQktfUNLF1STfWJURzXwTL9ogLaQkdF4Pabryd99AUcPUBtXS1zI1QVBJgoB4XiVbVcdsW1ZHoOs7qtmVA0UsHJL7wElaRYyBGJxTGtAK5jUyrkCYTCvzJXx3kRwgL8+MePcP8jj5MIukw6kkd27icUraW9fek8v8e0thWUSjbHjhwiUFNL69pNU2jghQY0OjzC7hd3MTY2SiaTRi9DFm669RYSVYlZviEpPSbGRtnx5KPsfP559u3bRd94jp/vfoVCsUBdbR2BwHT0UpbD/M/vOcl9D+7htRM5Xj6R4Yc7ujnek6VlURVVZ4l2no8owLFL5LJZSsUipWKBYi5LX1cXB/ft5diRIyxpa1vQT7T7wCnu+d52li6uYk1nMzt2HeJIj03nqpXEotMvQC6Xp7+/D096SG+69MjZ+l15Hk8+/XN+5w/+kK9+8xt88zvf5XRPL4sWNVJbU40mZtcw86TH7lde4yvfuB/dHaU2rvjk5+/lZ3tO0XXqNBdtWnfWxajm+CzUHJ9G5Xcne0/ymS//A/nMEGYizM6ukzz89HaWL1vBotrGM45rfGyccCTMoqZGntq+g3UXrJ1177n/fz1SOY7ouk4gGCQYi2PGatEMi7ns7JVxjUyM8dWv/x8a6mpQIQvbirLn8HHqmpdSV1OFPodIVboepWwOt2TjOS5OoUh+fJLcyARIhR6wpohRVRmwWTh+nOFvfQ+9p4fwpKRxYIzJfQfwrABWSzPGnH4pKclnJnAKWWSmn0iiFkcLIqVXzmusRI0pKyaXTMbmhb0HeeKp5zA0yTgRDvS5TIynqWusIWDM99c5joOSEtMqR0Q1Dcexz74pnp98+kx/OC9C2Ew2x5NPP8Opni5+JLMYRoA9rx3mgov3ceONyEuInAAAIABJREFUVzG3HJRSimJJoqSgbmkn171/BQooFB0QLsGAiWUJZmp9P4zfiCcldck4UnnYnl8JwJPeAr0TPPnTh+hzHBrWXIi9uI2cW6CjoZHB/l6e3/EYN775bTPgDoL+kRL/+s39uO4IyZBLVWIMLbqIJ3d1ky3k+MzH3jxvHDP7NzUrM6zg2Q9G8fRjj/K/PvcPlEqOX0BO+tEez3OpbWhk+cqVtLZNl3xWCgq2w1999j76BgbYXxvCweTF3lrShw7RseEY776thkrpkK/859c4cfwYBw8d5+ZbrmPVqg6WLG6hc3Xn7OTfOTI8OsrffO5zDA0PTZUOevDhh3jhpZf493/6HFsuvHDW523b5Y//8gv0DwzzYCBAMuKSLXmkTzzHscP1/NZ7foNIyK/VZNs2J44d8xmHlE+tnklnMHSDSCxKIV8gn89TLBTYduWVhCN+9E9Kj6eef5rXjh0lU1dL+vAw3cNZGhpq+PJXv8i//e2/EgpMlzmpPEulFK/ufY3rb7gGpRQdnSvJ5fJEIhEfSiJmPL8Zm+gU1GTeFPlRuFlKD1CTvYRUDk1zITcMwfisflTEwWNobJx9Lx3iLevW87MfvsTS5iU0xSNUWRJX6JhUEDe+Unzyv77PkfufwJElDCNI0DIpOUVMLKLBKG/6/B+TWNI41VmpXPruvZfhx7bTcNkl9O3rQWTTJONhootCCx7i7GIe6di4wsCu34A0DCIovGKOooJQmZtAKJ/ncngsw1//7dfoG9XJZKIcMYMEYwHEyaM8Z/TxatcwH333TQSMspO+PAWa0KbyYpWs0J/NXt+VtSRnPZOFpbKxn03pnfOoKFBMTqY5dOQ4JhbKrGIiM4nrOPz858/znnf9JvX1swkEXFeRzTmYpo5lGmhCn6IUd1wbKcGy5hAblCMnju2gdHxUuifLVRLnH5jGRkbYu+sFRpWGZlgEQhFMVzJ6+CCvPv8cHYtbuO7Wu9H16cTQkZEU2YkhLth4gsW1gxw93sAVF+3isZ3raKpuxhPTE6KUIpfL8srevWi6Xz9IlYkpdE1DKR/tf8H6C6YnWUE0nsCWipzjIFAY0kPgV3gMBkPo2lxCB8WJrmHGR4YIRKoZzXl88cE+PKcKz+3niSdf4F23XoxWPjdZAZP+wXFGhkf49r3f56qrtnH1NVfQ29cLCDZv3kRdXd28nLWnn32Wvr4+P4MAAZoPGhwdG2NgeHgeZsSVCruQAekQskyEDrF4DM9L0RSHowde5sKLtoFSjI2O8dnP/B3RgIklSiCEX75IaGi6QdGWaAIKDqzfsIFwJIIEvyrpq7uxCwUcQ2e0mEUqh5suvYkfPPRDSm5mtuKSilf3HOC5Z19i//79HH7tOPlcntPdp2lpauGa66/mkssvnMIdHfvpcwy+dICwEUQPB8hmMni5EpouCIVjmJqBMDSi65ppu+myWRFxT4HqfQm14lqEHkDs/i+8iz48j1MRQErI2zpHrTiLMlGWWfVck1zHgZ4TON1pgktnr6nTx0/Qf/QkvSMDaJrPCBQKhHBtD6EEwfY4IljBRZataqDlzZdgNCcY78kxePIUi9qXUL1hJcF4ZMFDXDGTIpPJMDaZRzc1pOeXEIrqNtFoCRLVUy0oFEeO97PzpT0kEouoStaD8IHEa9aMc+rEILtfGKfr6o2sXNowtZE6donJ8RGErqNXfNHSQ6EwTXPKCqvI//iTTzPU62dJOOk8ofooSknstI0s2Fh1US69/BJ+9/feP288M+XsFpcClGT37j3kMpNYwSCeZqCMALquk8sXcFxvahebeuiuQyGbZjjv0NzUgGUqinaGwYHTWFqE2pokKtEIc07NmXQGJSUlT+F5vib3yrWl5p6wnWIeHZerr77OVxKqgBQSNEH9okZfYYlK9r9/XeeiAB+6osQTQ3Eaajz0XIBsvoFbNrZw54UldM9DzTh2bd/+GLl8EcM0iMeiKK9EseRQLHlIJRkcGODo0UPcecfdU+zXpmlgagJT10AqhK6QnkQi8JREm8PUq4Bndu5hcVOEPEnGMh6uXcSTGXThMT6eKlsOvnL80Ic+yPU33MCxI0d48uldHDm4nwMHj1JTkyAYDLC0rQ2pNGqqE5iGORtTNXVEA+VJhFbeJV1vjttDoZsaf3HXOg5nojz74n4CWo6+iTyBSIKP3b6RlcmyBYNASo/MZBozFsLTJSiBoes+W7Km0VAXJaDZHOwrMg0JBkdJHNsmEUug6TqZUoFLlrayOhngms1bCVnJWdCEiVSKv/nkP1HMKSKhMANde1DSzwcd6j7M/r3HaV/1tzQ01qFpGrnuCSb29JE2DKT0wJBEG6tw0kUyhTE/WVoIalzJshs8xAxUuEChok2YvS+CFUMFo4jUSUTdQpVWIX9yP4v6X2VnKMZEdoLxYI5wfZBAxMJTHoYwKhoCywpwaryXvtQg1VYMNEXBSBM2okwWM6zuvILxsVFi9dVTj8TVNFzTJX/4BF5fmmapYQyNo+eL5c/M39y9sZMIV6OhcSnDw2Mo5RGPRXBGhilmB7GqWzCsUNk1IPj+9x8jEoqQnTiOjkm8to6CJ3jtYDV9Pcepqw7T3T3CitbGKavVdR1c6flHYelNrVEpFfl8jrgZ8GEXQuA4DkNDQwwODyNshdIVmeESSioKQyki8TjOYIGjR46dE5B6VsUl8dMqklVR1i2rY99AnqLt4njQkIzzofe9g1g8Wq4QOX1d36G9DB87SM2GWwCJEDqO4zI81EfU8cjs66f6bR+ZqiVfkZGRETxPYpRTcJSq5A7OH0BjfYJbr7uE8WQDQmroEQtDM/DyeToWN9Fk+alJMyNUXrqbaKhAfqiZoy/VMKLHGD4m2Lg8gqsM7NwkVpldRgBLl7bR3dNNX18fg72nyEz0c7J3iFwJNqzfQOeqVbS3r0CbsqIERbuE4yk810MoP5fOQ0MJDVtoGPp8cKztKbZddiEvHSkxnhkGTUdJF9MKkEpnkNIrH8d9/0L78qU0NzVw7XVXs++1w/T09OGUslx62VYKRYejx09wwdpOEvFpxPP6deupiicYGx9DCB0hfKexoRuEQ/MDE6byWNpcxc93jpAuZCkWJEppRKpMhG7heDNKwijfQWsZPiGwPtPMV5KJdI540K9t5n/cD2c7tsPW1lZ+PD6O40mU8uiaTGNEq/nw79yK0GYjtn/68E85fGoPhggRMEOU7JKvCAKmr2g0l5/8+CF+6wPvKdfiB4kAT2IYFko4qIiBNymQHmgSP31NMHV8ngYkC9T4MbzcgI8Nky56aGGiUgN45PBRlq25hG4mOdh7jBdeHKfGCvF7Y6toUdMZFkqALhVV4SghzSBsWNiyhObqBA0DM5ggPTxGJBadvgCFhsRLFcgc7EG4EqEU0k4jhYVQC1VzVThjfQTq25GGwg2HqI1a6LqOWd9G9vBThAo5X3FpgsmxHN1DQ0ivhBAK13ORnkQTOtIVRMIxSk6W3a8c5urL12Cgl7FgldQuiSrXqheahgYU81nC4QiG6dPxua6L8hRysogVC6I0n/PUzefRAxZuADSpETmPKqjnqA7hgbDYeuE6no7Dga4CnleFZQVpa1vCzZddQCg8DXyrZL+ffvnnLF61HqEb2K5E08AyLHTh4do57PQko33d1C9eOqu95uYWNl2yBc/1cw2lJzEti3g8Nk/7Kinp2HQ5P33lJEW7iBzOIR0bvBJGLEpD+yI0pc9waAhcAZpVRUrE2DVejRKgXJvhvMB2TWLWDPolIdiw4UIuuGAjUkpc15k6KoIoJ0jP5g4ERVVVkpvfdBueUOiawHUc8qUiruNRnUgSnUNaKlAEwxFyXpjjJ3aDkD45qOdRtF0iYX+hV+zNSlvhcBilFBduXMvqzna6uro5cOAghuEj0iOR2VFYx3VY3NxCfV09yeoklmWRSqXI5XIsVFJayQJYQZ7bc4zxnIZSwrdsMmOcGJyg4/LpZxcIBbl02+XEAoqCM41AR/hcf7qmEbJ0ksuNcmlefzHGLYOO6jiiVMLxgli6yaSXQ0WrUZo1i0pTKUUsFkPXDWw7jytnVMj1TAzdwNQsdEOfypyoWdXM8IkevEIRJTSKbgmZLWFUhQk3hEAXBAJBEu3N8xxfAjDX3oU3egjii8EwIVTDXBGAUAK3v4/HnnoCJT2UJjCGUtz69rdhluzp9SH8NVK/pIX6xiZ2OlkKdgEpFWEjgO06vtuh6zShyIzjn8CPnk9M4rl5NNdX/IYn0KqbkLZCn2dwCXK5ErXBAFowTEdjBJREeg6OdCg5ckZ5Ht9SioSrWL++juefeZZowsNxiuiYaLrOZdsu4vDh/eTyOT96KXzLPT0+goYC6frWLwIlfM5VKT3f0sU3HyzL4uqrtjHWOUZJuEhH4tgOdq6Apyls6ZfKWtnZcVZrC85JT6YjlEvf0BjP7TlINm8STNQRDgWYLClEdTMIfXrCyjRRxUIBK1oFgRCOLTENDYXA8Sz0gI2nWbh2aZ6TdOu2rWzctGEaKa0Umq4vyBEnhYXrltj/ysuUSnmwC3iArmu0X3kZUjNnFUtTQLS2hdH0s6Szg5RciZIueDkOFRTpLe3UB+ZbHpUE0vMhpBDAmvUb6Fx3gc+AjEAq6SfESt8pac1pw1OK69ZU8fEvbMcp5nEchWsXUFKiSZe1Hc1o2mxH59y+hcNhOjtX0dm5aur3c2VlWxtf/fd/m0q38Z3qDp70iEbmWxKGGSYYa0APxXFTwwjdQEqXi9traasNEq5pmmqnurqaP/iTP8LPMiifhyrJJ2Wckc8ILXwfEb6TQAqdI90DOHYJQyqkgFwqhVtm1JmpuYQQXH/DdTy383mKxdJURHNqboQgYAV46x3TAZYlV66ncUsHypsu+VJR/kLzrxGaQDe0WQoSIJfPc/DQUYp5B1ceRimojgTpWNZCqKZ5qpCg3zeNd916KUdOn6SrfwQ8j0A0xv4Dr/Hbv3EbQqpZEChN19hwy9XsPnWEoGnQaAZRjkfe9WvDbbv9NsJV8Vkvr+45nMh4fDpVwnWLKCWIxuNc9NRO/njdWsJz3xAFtjA43jOCGC0yOjqK67oUiwWSUYtVDUsxgpHymCEcDiA9j6bmpViBBFJ5lEoFTOWhWxY11WEMTWEX7enlDtQ0NJFJTeA6RawytZjrOqBpJOqaZtTP86Pfv/mBd/hR1fJ6kEpNsXsppZCeN2+NLCRnVVy68CnnG5qa+Yt//g/+z9fu4ZV9R/Cq4mhhCyEstLnxDKWoa+9ESo+aZIBC0cV2PVwX2tsvwNBcTo6lqAQXpi1z/8UOL7CIFhTp4Y2McKjbr1GeG+sjnKwnFgywyHRILGqbE7pWiECENS0W+cGHmMg7pMeGqapLsrq9iepAx7xQ9+uW8hhmqjhd6PMc8sz6u6C2uYm3XNNGKdzI/oPHME1BXX09dWaWqzcvxdDPHC30mz17n4UQWJZFbc0ci+EsUy2VQRxBQs+TqwowMtBNIhTk/ddfxarNVyFnjEnTtDJD+fmKmsJEJZavoXVsnFP93ViRIGEj7Pu8FDCHVzEcDvMPn/v7825FM3SCsdfLA6iwx/s58sLP+eSXvsXg0BjpdJpoLMotF6zgjz72IUI1LdOfFv4pY92GLdx+yyj/8p9f59KLt7Dv6AmymTyJeB1K02cpFSEEqy/cwBe++r+nIvfnFKHR2FJHoCZBakxy22238MjjT/Pi/qPoygHpwoyjNUIRr27kq1/5Ji8cOU1Pby+FfJ5QOMztWzfy6U/84RRruyY0ApZBx/JFDHR3kUwmkFaMYqlSZUVnfCxLNj1Joap6CrAthF/DLlnXMBtwOnUCmzsGMS+P+BeVc0cVhSAcDnPpZZcxODhGJlPECga5+cZrF7CE/K4uXbWOnr0vsufYIMeHsoSClu+813Q62lqoi0bR5NzSM69PZDBMNtBAfU0N0snTlFzOZEnRsWwxyc4NaJHk7JcFEJpJon0LTY3PE5iYpMqqI1pXhxmtRi7ajGS20oHZkIiF5mYhmbrGNy3O8XlBNNlAbetyqqqTFHL9rGxbCWaAuoBNKFROTP9vFIFvuSY7NrKo5SlUtsSKxUlGJrI8enKCVifMil8GGasEAolhmLz9rndx4YYL+YOPfog6LUB720qWtLQsAFmYMX9TuYv4vqiyI1ecoU/Tz0NMXXtmZS8gEKF66Wpqq2spFmyWNDeRKxY4UYTEiovnMVEJwKxZwrXXXsdXvnUfDQ21rJTglAqIUARR7pjruoyOjr7OyfJFKoWXXMrmSzbz4x8+xNatF7Nr916y2QxdaZsqa2SWoldInKrFZDHo6eunvq4eUzcYGBli+6tH+LBeQ2FoGIEPQBVKsnlVA9t3P8xkXy8ZvQ5NmVQnq/GMIK/uHmVDRzO53DhDQ0NngKK+sdLY2HjGv501yZoZy04pn/DSKztYp45Pfrhq1jCU9Esiu57HkaMn+NkTT3D1lVeyunMVumGgC4XQNIT4xQkmK6al4/pna6EJ/7sQmJbv9JtfqN83TdOFIj/9wb04JcVtd91VLicSQujagopLSklvbzePPvYwHe0dbLviKr8y5Rn6XiHU/MnDDzE2Os6dd9xOIpE4I2BTKd8Z2j8wwKMP/Zj2jjVs3XoxlmWVgaG/HBHn65dprFrJtlGKMhjYh1abhnFO0Os5bl/5gsLHc7001MOAU2Bb3WJqgtHpVBYxf/OQ0iWXy/HjB3+CUoqbb76VmprqeYprLvj1uRef58iRo9xw3XW0NDezsOYtU6gqnzatgnqXUiJE2bIU85dt5X0slUroFUwTELCsshIWHD16lE2bNk0R+L6+KfOtVM/zcB2XQDBQhhgpAoHpNuZ+3nGcWVwDlbaDwWmwdeVJKE/iuQ6uLFeOmxqn7680dQOp5LyCmL8qyeVyZ2zkvO02UTbzzsfUE5qGLkyUEJw4cAA1eownH0yxft2aKYafX1YqR8vA66A0qoQPDrz8PLt+cg/jY1mkV+Sdv/3hsq9k4S1bofjal/+N5mCRL/34e2zcdBHx+Jl5BXVd59lnfs7D9/0nli4o5ib4yEf/6Kw9UxL+4wufxR45yUPfu4e19z1EbV0YIX75TEqvXJr6/NNOpit4BM/D3zBXKkjvmW6AObdnap6VIis9fv6zh1mEYvfWy7mp/YIZn5t9vVIKhMHO7Y+x7+H7aGlqZKRzJdU1F5fnSsxztCtgYnycn/7gHsJakW90H+d//MXfMG9fq4wd/xaBc4y9Ms7KfQRilkJY6PP5fP4XUlxzpTAjf7dwllzeilQMjvO/RlZO9FNSKt9jfqHO/345B46rjMvypJ8mYMyu33MuBaQJ2Lh2NVcsM7h/x4hfC5n5yONfqag5PyjB+tWrWfzbd6IcF6ftUgzNKx8hzAU3EoHGbVdcREKmWLZkCQHT5Fw7zupVK/nw29+EKKbR6lf6p8azHFM0Xef3PvQBAmOH2d81QDSolXf+6YjU1CjOedyZ/dlcLkdfTzcNjY14UhCwTCLRyBmtpoUzBs7dTilXouvASfbteIX0yDhogguu3kxTews1LXVYIWvBI0besdm4ro0NbokfpMfK+8fMTWT2CpJATW0Dm1atpqU+ju4WAI9KzYB51pBUpFLjLIrD2669gj/64o/o7e2hpWVxOaQ/RzG+jnEf6DrCZ775zyyL1PH2G25n3ZoNv5w1+ms5Lzmn+TQ4MMi/f+krLF7czN133U6pVCIajRKLRc+QPjFtNk+mcuzaO87LOw8ykdG49EgfrcsaiERn4jR8zFMunyUSjiKVBBRmJen5l37+FVywfyNHKVJ6jFfStXSECvzn0RjJgSHu2JBkZY0xi994Kk3BtWnpXEf+8A6i8Rip8XFq6gz0ci7W3JfU8yThWAItmiBk2hgRQSo9SSQcnleCuiJSeTjBBL1D44TiSbIFhRH2fIyYmFGTSil2PvYQJ0+e5NrbbicQihAIBIjFomdcLPv2HSQ33gfK5flnX6La0ClEw9xxx+1li3X2mCcmJvi7z/w9H/ydD1BTW0MymZw1zoXa8VyXPT/YyXOPPIuGjhUKoHR49t4dOK7Dde+7mQtuuHAq/47yUaokJQczY/yw+zSjwMDSKD2FSRrDMUyhl92E/juB0kHYuK5HvKmVgZEc4Yggnw9QM5knHg1jlo9pPnDVf/625zKas8mFotjJOqpXrOJUKkNNg0NoxvOYfm/H0DWdcNSHX8wUMXsHIRoM8dKhVxlONlC4P8v7NYP1a9b/927O/z+UsyZZS6U+9dIzO7n/u9/m0Mk+tm9/iq9//r946VQfK5Y2UVvnpwz4pvLsh5SezPGPn76P44f6yRU1dM3k5RcOk0pl2HDhNCmqUnD88B7u+/q/MDLcx6HXXmTfrp30dPeyeFmbH/lY4P5nswrmJfjim7gugp2nCnzu8XFeM9swlgU5VliEdCS7u4tsbg4SsaaxWQrF4MAgA92nKNgSqevYVpySJ8hPjhGMRDGtwIyx+P6w7t4ujp88RkGL4gbryMowk5MpMpkUdXX1lU5TsSQymSw9/UNkSyDCSWLNHRSKgsn0BIFQgIA5vXhe27ef733v+zzw8GP84Fv38L0f/YShoRRrVncQic4LiuN5HoVinhNPv0AkNUr7ZRcxki9RLDhMTEywZMmSMnRjWva+8ir33vsdPv9Pn+eRnz4KCLq7eolEIiQS8QXn286VOPncEfp6+7FLJUzNLxppmgahcIjSSJ4Vl61CL49FAa6U3Nt1kPsHjpOORBmuStJnOzwz3E1rNMGiULRycAMESrgc6svxXzt6+dJPuihJqA4H+e5AK48fLjJpW6xpsjD0CrxBoDx44dQQn/nZixzOBnjkeJFBq4adfeM0VEVYXpdEm7ExdJ08yv/86Lt55omHSY2Osuu558jlcixuXTpj3P5RWgFPvPg0P372UZqq61het5ixsVGWL11OMDD7yCiET5T8xS9+8awBn1/LtHzqU5/69Jn+do6jouR42qBx5VYmjr5GevQkTgh2/vwJXn35WT7+px/nthuu9OtZo2Ypo3SqyMnTPdQmG4hE4iAlmfw4uhZAzApzKw7s2YFnT7J311MUiwXskkfICOC5OW6+4z3l9J1Z7n9e2rWLyVSq3OY0NsnzXCj7GiKRCGvWrQVcpLBwnCLPDkpS4QThAOyZ7CQQ0clPKvoGM7w85HFjuIjQgr6TXikmUwVcxyYcjaPF19GgK6RyGR8eITg2RDgSm7W7ep7H4PAwnsTnvotGKYyMoBJRNF0wmU6RiCdnjSVVKDA2OUlNVRJPq0M3fMqHiVSOaKxALBSikvryzDPPMjpZoKW1naHBASYmJti7dw+PP76Uu3/jdixrdka+ADQ8WjrbOfSNb1D/s8cpXnMTycZGLEPj1Vf30rK4lfq6abKFF154keaWxTiui6ZpfO1r36CutoGhoRHe8c67SZRrlc9sRzd0bM+mZ6Qf4UHSsVE66IZBwAwQC0d9+vrKXAl/8b84eJqCXSReFcdWgoQpGM9OYkt36rRY4a3xXJfvPNbN0ycyWIlGTifqGdBAD0RwMfjZ4Qy3bQpTZ/gwE9CRAnoyaYYHB1jR1sjqhjiOCrDj5JiPnp9xBrVLNg9++x5kThCuqeHEsZO8+NQTTNouv//xP+ctd72NYHgaXiGAU92nyRcKvHz8ICC4XKznrz71CT74wQ+xqKGZ+tr6X1tevwI5e8qP0pBahILVSsslDRw+cpR0135KBRe7MMFf//Xf8+APf8Tv/8576LhgHVWxePlIoTh6pAepBLpuoGk6SnlIqeF6M3Ib8V/KsBXFKflWkWVZTGZSFOwSu198krGRPpavuoD2VeuJxZNEYwlKpRJf/+rXOHTwELqm4UmJaZhoml9CUGgawUCQJUtb+ad/+TzCDGLgkncF3nA3gXEHWZPEsKopSkUqX+CDK7JUv/otxJJ3TjFceZ6LdLPksnnC8YTv4xMKXEmhWMApFXA9d4r7bmpSdZ3+gQG0YA1kiuDYZNNpXOVHHJVSU/5pT3rkCwXGxsawdIN4LI6h69iOzdDQEFVVCaB66t6e52IYGqkJn3R2SWsrExOjPPDAA/T0dLO8fTmdnR00NjZQV1dDsVQiGLCoblvGxF13MtY3QHNrM8MTaQ4eOkbR9sGxWzav5vrrb8A0TRoaG9B1jXg8jltG8acmx9m+fTs9Pd2sWtXBxZdsIZGI09DgW5CTk5PgSZqaGtBNg0Q8TqFQJJ/Lk5tMk8lmSI+lqIuUWZSUAk8RkB51wTBGKIKmQJaKLDbCaJ7EYZqqVQCuhFePdDPaN8yittUQq8EzBBqKUi5POptmKJWkPlQ5YioQir6eAdAMwinJ4kSKA8MThPQGhHTKYEK/jWKxyJMvvYIslJA9vdSWbFqWteMcPcR//OPf8sxjj3DHu99Hc2sry9pWYAUs3nbT7TTVNfLd7T9GaYqezChHJ7r5l3u+TL5U5JL1F/GBu9/nh4V+bWm9YXLWo6Im+FR7S4ym6gCbNnaypjVBSUbI2zqGVsRzCvR29dB/8CANyXqWdlSOgC47tr/K2EiBcCjs038JjXQ2S+uyRjZe1D4dcUKwZOUFLGpuQ0iX1OQEpWIex7bJZ7OcOnGC/a/s5sVntlNf10DzUp8R5PFHHscuFrGsALoGDfV1hEMhXFdimiZKKVoWt3D1tdf4EUOpIbwCq4ZfJJNxyEabiEd0gsqjYOtc3yYxBl+hYfVWNM23WjShEc6eJDV0kkCoDq3oIks2diZD0J2guthHePE6v3xNeUCO6zA0PExqMkUkHCaCRzASwAoGQbeora4hHApP+Xpcx6F/yKdUi0ejBC0LXfNzO6uSSSSSuqTPLCOE4OKLt3DZtkupq6+ju7uHydQkiUQVgUCAXbte5MEHf8wzP3+BZFWC9evXYlkm8USS8YkUNhqdmzeSymRpaW5geXsry5c18/LuF3n4kcdgNDb3AAAJZElEQVS49ZZbCIfDrFu3lq1bL6axsYGJiRTj4+OUSkVGRoYZGhpm9+7dPP7YdorFElu2bPbzSj3FsZ0HyBTSZGWRcF0MPWSieYpkIIaUiuWbV5CoT5YtLgCX+vQY+8qVKgQC7BLvi5qsrm4mEIiW3xMPVImiFGx/9GXGek5SCkQJxmowNR1HgJYfpXlkJ1s76qhrrC9bXAJPKALjBzlWtAhpARY1NPNCyuH2FpcrV7cTDVdNR6gDATZu2ky8dhHHe/qws5NE4lVEYglKhSxj42M8tf1nbP/ZE1x53fUkk0mioQgrlrRx0yVXc2n7BmKJKloXtXL44BEu6dxEiCB9ff3ki0XCoRBf+tKXfq3AzlN+4aOiEIKqRBVXX7kZpSCzcgkda9dgaiajowOMjY7RNxniii2rWN46XfTNUxrFvJ9knM9mysmyHs3NSUzDREqFrk/jdCzLYuXaTbSv3siWruPs3fMcXV1dHD50kEIpzeLGxWy6aAur1m2a6hcCiiUbwzAIBSwK+SzgM287xSKu69C6bJkPAVAKlIbrlsiNniTReClaNMGEdMjaBhNugHSxwMrmNnyap4oSssmlUxSUQTxZhWb4Ck0WDKQ7QdEeoeL4r1hchm6g6zo1yRrCVTGCRRdbA9srohsaYk60XinwPJ/QQNMNio6DiaTklRBCIxKKzAJpCCGoqanh9tvfysUXb+HRRx9lfDzFSy+9ROOiBq66+gqWL1/O7Xe8ZeoqTRN0dnbS2dnJyPAQA109bH90O5OZDHtf2YvnuPz95/+J6urqqWfY0NDAm998G5dcfDGPPPoY6ck0zzzzLGPjYyxbtpwtW7Zwy803TqVC6ZqOa7uU0gVOHzvCwKEuDNPC0k0aE7WEY1X0Huxi8ZplU2MvpgY5ns9TDOiU8hliRhAXUI5HSEwvbg+FUoKQdHhL9R7e/NE38XcPpZGei+e5aEaYuqDN9cs8il2voa3v9J+jAKEEIVMjr4KISJSDGQHhGtqaYtRFZxOXCCHoWNnByhUrecc7386OHU/T19dLLp2mtr+DkuMgPcWKlR0kq/3jvmEY6HqEcDhMTbKGppbF2HaJy9ZcxOHDh8nl8j5Mwvqli+r9WmbIeSHn/e8Qj4aJr2hHKVjc2oLnuei6hmHMfiiakixqkPyg/zk8CboIEI0m6KheTmtLEE2TMAfqWcm7a2nroHnZSmzbplgoIKVvQYXC4Skckq4b/P7H/j+y2Sylko3rOEym00gpCYfDBIMBLNOgubnFBw+qcq6aUozlPAKtrdg5l4CwCWhBwsJloruHRRtakUJHm05pxvEgVwww1jOGZhp4CoqFHGK0j0BThAqx+szwgdDwSTqFjhvU0PCgWAbE+nBv/4PKH7dlGhw/fpB//td/IFGdJJ/PMTo8zh1vuYt3vu2d8wKrlblubm7m/e9/P57nlZOlBaFQaMGCgpWf6+ob2LLtclZv3EQul0UIQSQaJVlVNc+ZDNDQ2MB73/tupJS8933vxnU9gsEAobLfreJ70wyN4cwELx95jfHMBAr/yBwPxcjZeVbUhGnubJ3VJ7s4yYuPPU+vDmYyRiEUBMdBbdyA0Ka9T5rSUBhM5scp2ZKHHz5AJLYWTbg4jobAJhQJs6i2EaeYQ1MSlAbCT0hzSy753hNMAl22h2kZHE6s4MpVS2BOIYKZ1tcNN1zvg5wdB9v269qbllV2S2izrvH9rBo1NT7BalVVkmXLlqGkIhAMEAwGOX78OL+WN0ZeV+LQrCx3oZfBpPM/J6XHhvYAmy9eyZM7drJ82TKOdx9ljQxxYUfC932dZfOpvDiVZMspNE8lWoigY+VsTrwpqbztYvoaTYDUFaYQNC9pQcv3curlZ0gNdpPKF0GapDY3oV3zpnJ6hi+60AhHE0w6aVKjw3ieh67p2MU8bWaJxhVXTDmaK00rJFWWxpfve4Cq2kYi4TCaBiPD/dyyYSU0Lyr3CwQKw9BpigpOvfYKPf1dnDx1AsOy0IVO4fRr1IYXqv46W3RdJ5E4MyB27tyGI1HCkSiISgHIc1sCmub7vObeq/LdDAa440/ewWXvvcYvK112whuajiYE1TU1NC1unhUdDhgmW5c2suM/fkBqbIRovIZgJIRcuRLHcaZeTgFIAeFwDGlVMZQPIkIudjHvVx3QkxT1KrxQgkJ+FKWXQS1KIRUsS8ZoHTnCK/sPsWHdOg4ePc7e4X0ULu0kGhNnHP5MJVZJFj4fq6mygSxUHODX8sbIL5zxeLYHqOkBgnVLeNNNN/Hscy9x+5vfxNe/fR/7jp9m3NaJnmdi6Zksjdd5EUppaCg0oTM2XkCrkSTIM2iXmBw4RUN9MxgRRvOC2tC0o13TIRyvYe/Ob/Nc7wSjXafRNJ2axkY+ct16NurmPPS1UApn8BBaPsUDj+1noq8H3Qqxdm07t17QTixZV+5medFrAj03xOrmOn4KaJ5Gc1092WyRWpVHFSchfubI1C90/BDz/nP2j5/XYoWGxY00Lll0fn1QAtezWFRfT9iyGMzliMWSBAMB7IkxlNIQ+KknotKAMInV1xOcTJL1HFSxgKGbqGIBJQ3a1nbQ/2oaz/XQjIpFJNGql/Ce33wnu//0E1yydRP9I8NMpLMYkSSU23gj5uCNuObXcn5y1lzFT3ziE7+4F1GB63l8/0f3s6ZzFROTGfr6+3nrbTcTDr3ejP03RiQKJW26B8bo7x6gUPCIhDUmUmmqqqtZ3raEhprqGetZoqTipd2vcOjUaeJBC0+CC1RHQ1x/3TUsVMvKdm22P/k0o6k0tYkYvf+3vTtmaRgI4zD+T6Q6FFQ0TVpwcS7qF7CDg24FlX67fAMRwcHF1YIuDgoVxMVF2iwpghSEJOdQLRalOmjxlef3IR7ujnvvuonKi/PabmwqHHuhYXg+5opM5+0zXd/eqbIUKKxG6nSuFAY17Tabo7tP/00uJz/PdHxyqqTXUyWq6mkw0M5WQ2EUyh8bGnZyWaabi7Yu71Plfkl54anwnTz3rPLsjGoLc1pdDrRS31DpdbUzfD1W6j/2dXB4pPW1uh66ieRytfb29XGe9fekaao4jjmc/yb3NjbyiYnh8t7vmwBgiiaF62c+ewOAKSJcAMwhXADMIVwAzCFcAMwhXADMIVwAzCFcAMwhXADMIVwAzCFcAMz56kNYAPhzWHEBMIdwATCHcAEwh3ABMIdwATCHcAEw5wUa2drv0Q4onAAAAABJRU5ErkJggg==\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {}\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Preprocess documents**\\n\",\n        \"\\n\"\n      ],\n      \"metadata\": {\n        \"id\": \"hfw6f5aBvhzR\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"def preproc(d: Document):\\n\",\n        \"    return (d.load_uri_to_image_tensor()  # load\\n\",\n        \"             .set_image_tensor_normalization()  # normalize color \\n\",\n        \"             .set_image_tensor_channel_axis(-1, 0))  # switch color axis\\n\",\n        \"\\n\",\n        \"docs.apply(preproc)\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 281\n        },\n        \"id\": \"YKG4TgKDvn-l\",\n        \"outputId\": \"da73230d-67b2-44c6-8377-28db2d292e36\"\n      },\n      \"execution_count\": 36,\n      \"outputs\": [\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"text/html\": [\n              \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\"><span style=\\\"font-style: italic\\\">                           Documents Summary                           </span>\\n\",\n              \"                                                                       \\n\",\n              \"  Length                 500                                           \\n\",\n              \"  Homogenous Documents   True                                          \\n\",\n              \"  Common Attributes      ('id', 'tensor', 'mime_type', 'uri', 'tags')  \\n\",\n              \"                                                                       \\n\",\n              \"<span style=\\\"font-style: italic\\\">                      Attributes Summary                       </span>\\n\",\n              \"                                                               \\n\",\n              \" <span style=\\\"font-weight: bold\\\"> Attribute </span> <span style=\\\"font-weight: bold\\\"> Data type    </span> <span style=\\\"font-weight: bold\\\"> #Unique values </span> <span style=\\\"font-weight: bold\\\"> Has empty value </span> \\n\",\n              \" ───────────────────────────────────────────────────────────── \\n\",\n              \"  id          ('str',)       500              False            \\n\",\n              \"  mime_type   ('str',)       1                False            \\n\",\n              \"  tags        ('dict',)      500              False            \\n\",\n              \"  tensor      ('ndarray',)   500              False            \\n\",\n              \"  uri         ('str',)       500              False            \\n\",\n              \"                                                               \\n\",\n              \"</pre>\\n\"\n            ],\n            \"text/plain\": [\n              \"\\u001b[3m                           Documents Summary                           \\u001b[0m\\n\",\n              \"                                                                       \\n\",\n              \"  Length                 500                                           \\n\",\n              \"  Homogenous Documents   True                                          \\n\",\n              \"  Common Attributes      ('id', 'tensor', 'mime_type', 'uri', 'tags')  \\n\",\n              \"                                                                       \\n\",\n              \"\\u001b[3m                      Attributes Summary                       \\u001b[0m\\n\",\n              \"                                                               \\n\",\n              \" \\u001b[1m \\u001b[0m\\u001b[1mAttribute\\u001b[0m\\u001b[1m \\u001b[0m \\u001b[1m \\u001b[0m\\u001b[1mData type   \\u001b[0m\\u001b[1m \\u001b[0m \\u001b[1m \\u001b[0m\\u001b[1m#Unique values\\u001b[0m\\u001b[1m \\u001b[0m \\u001b[1m \\u001b[0m\\u001b[1mHas empty value\\u001b[0m\\u001b[1m \\u001b[0m \\n\",\n              \" ───────────────────────────────────────────────────────────── \\n\",\n              \"  id          ('str',)       500              False            \\n\",\n              \"  mime_type   ('str',)       1                False            \\n\",\n              \"  tags        ('dict',)      500              False            \\n\",\n              \"  tensor      ('ndarray',)   500              False            \\n\",\n              \"  uri         ('str',)       500              False            \\n\",\n              \"                                                               \\n\"\n            ]\n          },\n          \"metadata\": {}\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Encode image documents**\"\n      ],\n      \"metadata\": {\n        \"id\": \"dxVn7Vy7x3O6\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"import torchvision\\n\",\n        \"model = torchvision.models.resnet50(pretrained=True)  # load ResNet50\\n\",\n        \"docs.embed(model, batch_size=8, device='cpu', to_numpy=True)\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 297\n        },\n        \"id\": \"H-vsMHXDx8ep\",\n        \"outputId\": \"76c9487e-4a17-433e-a94f-5e35015fdffa\"\n      },\n      \"execution_count\": 37,\n      \"outputs\": [\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"text/html\": [\n              \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\"><span style=\\\"font-style: italic\\\">                                 Documents Summary                                  </span>\\n\",\n              \"                                                                                    \\n\",\n              \"  Length                 500                                                        \\n\",\n              \"  Homogenous Documents   True                                                       \\n\",\n              \"  Common Attributes      ('id', 'tensor', 'mime_type', 'uri', 'tags', 'embedding')  \\n\",\n              \"                                                                                    \\n\",\n              \"<span style=\\\"font-style: italic\\\">                      Attributes Summary                       </span>\\n\",\n              \"                                                               \\n\",\n              \" <span style=\\\"font-weight: bold\\\"> Attribute </span> <span style=\\\"font-weight: bold\\\"> Data type    </span> <span style=\\\"font-weight: bold\\\"> #Unique values </span> <span style=\\\"font-weight: bold\\\"> Has empty value </span> \\n\",\n              \" ───────────────────────────────────────────────────────────── \\n\",\n              \"  embedding   ('ndarray',)   500              False            \\n\",\n              \"  id          ('str',)       500              False            \\n\",\n              \"  mime_type   ('str',)       1                False            \\n\",\n              \"  tags        ('dict',)      500              False            \\n\",\n              \"  tensor      ('ndarray',)   500              False            \\n\",\n              \"  uri         ('str',)       500              False            \\n\",\n              \"                                                               \\n\",\n              \"</pre>\\n\"\n            ],\n            \"text/plain\": [\n              \"\\u001b[3m                                 Documents Summary                                  \\u001b[0m\\n\",\n              \"                                                                                    \\n\",\n              \"  Length                 500                                                        \\n\",\n              \"  Homogenous Documents   True                                                       \\n\",\n              \"  Common Attributes      ('id', 'tensor', 'mime_type', 'uri', 'tags', 'embedding')  \\n\",\n              \"                                                                                    \\n\",\n              \"\\u001b[3m                      Attributes Summary                       \\u001b[0m\\n\",\n              \"                                                               \\n\",\n              \" \\u001b[1m \\u001b[0m\\u001b[1mAttribute\\u001b[0m\\u001b[1m \\u001b[0m \\u001b[1m \\u001b[0m\\u001b[1mData type   \\u001b[0m\\u001b[1m \\u001b[0m \\u001b[1m \\u001b[0m\\u001b[1m#Unique values\\u001b[0m\\u001b[1m \\u001b[0m \\u001b[1m \\u001b[0m\\u001b[1mHas empty value\\u001b[0m\\u001b[1m \\u001b[0m \\n\",\n              \" ───────────────────────────────────────────────────────────── \\n\",\n              \"  embedding   ('ndarray',)   500              False            \\n\",\n              \"  id          ('str',)       500              False            \\n\",\n              \"  mime_type   ('str',)       1                False            \\n\",\n              \"  tags        ('dict',)      500              False            \\n\",\n              \"  tensor      ('ndarray',)   500              False            \\n\",\n              \"  uri         ('str',)       500              False            \\n\",\n              \"                                                               \\n\"\n            ]\n          },\n          \"metadata\": {}\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"**Index documents into PQLite**\"\n      ],\n      \"metadata\": {\n        \"id\": \"mD9K3ih_zn5I\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# clear the workspace folder\\n\",\n        \"!rm -rf workspace/*\\n\",\n        \"\\n\",\n        \"index = AnnLite(dim=1000, \\n\",\n        \"               metric='cosine', \\n\",\n        \"               columns=[\\n\",\n        \"                        ('year', int), \\n\",\n        \"                        ('baseColour', str), \\n\",\n        \"                        ('masterCategory', str)\\n\",\n        \"                ], \\n\",\n        \"               data_path='./workspace')\\n\",\n        \"\\n\",\n        \"index.index(docs)\"\n      ],\n      \"metadata\": {\n        \"id\": \"N3mxWt_kyVOQ\"\n      },\n      \"execution_count\": 38,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"before_year = \\\"2017\\\" #@param [2017, 2018, 2019]\\n\",\n        \"category = \\\"Apparel\\\" #@param [\\\"Apparel\\\", \\\"Footwear\\\"]\\n\",\n        \"color = \\\"Brown\\\" #@param [\\\"White\\\", \\\"Black\\\", \\\"Brown\\\"]\\n\",\n        \"\\n\"\n      ],\n      \"metadata\": {\n        \"id\": \"kKHnOpzc17c7\"\n      },\n      \"execution_count\": 39,\n      \"outputs\": []\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"query = docs[0:1]\\n\",\n        \"index.search(query, \\n\",\n        \"             filter={\\n\",\n        \"                 'year': {'$lte': before_year}, \\n\",\n        \"                 'masterCategory': {'$eq': category},\\n\",\n        \"                 'baseColour': {'$eq': color}\\n\",\n        \"                }, \\n\",\n        \"             limit=5, \\n\",\n        \"             include_metadata=True)\\n\",\n        \"\\n\",\n        \"\\n\",\n        \"img = Image.open(query[0].uri)\\n\",\n        \"imshow(img)\\n\",\n        \"plt.title(f'Query: {query[0].tags[\\\"productDisplayName\\\"]}')\\n\",\n        \"plt.show()\\n\",\n        \"for doc in query:\\n\",\n        \"    for k, match in enumerate(doc.matches):\\n\",\n        \"        print(f'[{k}] ({match.scores[\\\"cosine\\\"].value}) id: {match.id} tags: {dict(match.tags)}')\\n\",\n        \"        img = Image.open(match.uri)\\n\",\n        \"        imshow(img)\\n\",\n        \"        plt.title(f'{match.tags[\\\"productDisplayName\\\"]}')\\n\",\n        \"        plt.show()\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        },\n        \"id\": \"pWlDhtu10GlS\",\n        \"outputId\": \"a29ad777-322e-4b3c-db7f-8361e8342bc4\"\n      },\n      \"execution_count\": 42,\n      \"outputs\": [\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          }\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"[0] (0.2998480200767517) id: 20166 tags: {'gender': 'Men', 'masterCategory': 'Apparel', 'subCategory': 'Topwear', 'articleType': 'Shirts', 'baseColour': 'Brown', 'season': 'Winter', 'year': 2011, 'usage': 'Casual', 'productDisplayName': 'Wrangler Men Biker Brown Shirt'}\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          }\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"[1] (0.3230225443840027) id: 20105 tags: {'gender': 'Women', 'masterCategory': 'Apparel', 'subCategory': 'Topwear', 'articleType': 'Kurtas', 'baseColour': 'Brown', 'season': 'Fall', 'year': 2011, 'usage': 'Ethnic', 'productDisplayName': 'Diva Women Printed Brown Kurta'}\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          }\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"[2] (0.3932587504386902) id: 20110 tags: {'gender': 'Women', 'masterCategory': 'Apparel', 'subCategory': 'Topwear', 'articleType': 'Kurtas', 'baseColour': 'Brown', 'season': 'Fall', 'year': 2011, 'usage': 'Ethnic', 'productDisplayName': 'Diva Women Printed Brown Kurta'}\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          }\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"[3] (0.4208575487136841) id: 2052 tags: {'gender': 'Men', 'masterCategory': 'Apparel', 'subCategory': 'Topwear', 'articleType': 'Tshirts', 'baseColour': 'Brown', 'season': 'Winter', 'year': 2010, 'usage': 'Casual', 'productDisplayName': \\\"Lee Men's Trademark T-shirt\\\"}\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          }\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"[4] (0.4633525609970093) id: 20258 tags: {'gender': 'Men', 'masterCategory': 'Apparel', 'subCategory': 'Topwear', 'articleType': 'Tshirts', 'baseColour': 'Brown', 'season': 'Fall', 'year': 2011, 'usage': 'Casual', 'productDisplayName': 'Wrangler Men Polo Charcoal T-shirt'}\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {\n            \"needs_background\": \"light\"\n          }\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"markdown\",\n      \"source\": [\n        \"# Using AnnLite executor within Jina Flow\\n\",\n        \"\\n\",\n        \"In a jina flow, we can use the executor of [AnnLiteIndexer](https://hub.jina.ai/executor/pn1qofsj), which uses `AnnLite` for indexing documents. \\n\"\n      ],\n      \"metadata\": {\n        \"id\": \"alDzKQMyFj3Y\"\n      }\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"from jina import Flow\\n\",\n        \"\\n\",\n        \"f = Flow().add(\\n\",\n        \"    uses='jinahub://PQLiteIndexer/latest',\\n\",\n        \"    uses_with={\\n\",\n        \"        'dim': 1000,\\n\",\n        \"        'metric': 'cosine',\\n\",\n        \"        'columns': [\\n\",\n        \"            ('year', 'int'), \\n\",\n        \"            ('baseColour', 'str'), \\n\",\n        \"            ('masterCategory', 'str')\\n\",\n        \"        ],\\n\",\n        \"        'include_metadata': True\\n\",\n        \"    },\\n\",\n        \"    uses_metas={'workspace': './workspace'}, \\n\",\n        \"    install_requirements=True\\n\",\n        \")\\n\",\n        \"\\n\",\n        \"f.plot()\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 162\n        },\n        \"id\": \"HKyVAQ3eH2ZH\",\n        \"outputId\": \"d433a995-28cc-483b-ce7a-1e57d2d7aa27\"\n      },\n      \"execution_count\": null,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"           Flow@2018[I]:flow visualization: https://mermaid.ink/svg/ICAgICAgICAgICAgJSV7aW5pdDp7ICAidGhlbWUiOiAiYmFzZSIsICAidGhlbWVWYXJpYWJsZXMiOiB7ICAgICAgInByaW1hcnlDb2xvciI6ICIjZmZmIiwgICAgICAicHJpbWFyeUJvcmRlckNvbG9yIjogIiNmZmYiLCAgICAgICJtYWluQmtnIjogIiMzMkM4Q0QiLCAgICAgICJjbHVzdGVyQmtnIjogIiNFRUVERTc4QyIsICAgICAgInNlY29uZGFyeUJvcmRlckNvbG9yIjogIm5vbmUiLCAgICAgICJ0ZXJ0aWFyeUJvcmRlckNvbG9yIjogIm5vbmUiLCAgICAgICJsaW5lQ29sb3IiOiAiI2E2ZDhkYSIgICAgICB9fX0lJSAgICAgICAgICAgIApmbG93Y2hhcnQgTFI7CnN1YmdyYXBoIGV4ZWN1dG9yMDsKZXhlY3V0b3IwL3BlYS0wW2ppbmFodWI6Ly9QUUxpdGVJbmRleGVyL2xhdGVzdF06OjpQRUE7CmVuZDsKZ2F0ZXdheXN0YXJ0W2dhdGV3YXldOjo6R0FURVdBWSAtLT4gZXhlY3V0b3IwOjo6UE9EOwpleGVjdXRvcjA6OjpQT0QgLS0+IGdhdGV3YXllbmRbZ2F0ZXdheV06OjpHQVRFV0FZOwpjbGFzc0RlZiBJTlNQRUNUIHN0cm9rZTojRjI5QzlGCmNsYXNzRGVmIEpPSU5fSU5TUEVDVCBzdHJva2U6I0YyOUM5RgpjbGFzc0RlZiBHQVRFV0FZIGZpbGw6bm9uZSxjb2xvcjojMDAwLHN0cm9rZTpub25lCmNsYXNzRGVmIElOU1BFQ1RfQVVYX1BBU1Mgc3Ryb2tlLWRhc2hhcnJheTogMiAyCmNsYXNzRGVmIEhFQURUQUlMIGZpbGw6IzMyQzhDRDFECgpjbGFzc0RlZiBFWFRFUk5BTCBmaWxsOiNmZmYsc3Ryb2tlOiMzMkM4Q0Q=\\u001b[0m\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"text/html\": [\n              \"<img src=\\\"https://mermaid.ink/svg/ICAgICAgICAgICAgJSV7aW5pdDp7ICAidGhlbWUiOiAiYmFzZSIsICAidGhlbWVWYXJpYWJsZXMiOiB7ICAgICAgInByaW1hcnlDb2xvciI6ICIjZmZmIiwgICAgICAicHJpbWFyeUJvcmRlckNvbG9yIjogIiNmZmYiLCAgICAgICJtYWluQmtnIjogIiMzMkM4Q0QiLCAgICAgICJjbHVzdGVyQmtnIjogIiNFRUVERTc4QyIsICAgICAgInNlY29uZGFyeUJvcmRlckNvbG9yIjogIm5vbmUiLCAgICAgICJ0ZXJ0aWFyeUJvcmRlckNvbG9yIjogIm5vbmUiLCAgICAgICJsaW5lQ29sb3IiOiAiI2E2ZDhkYSIgICAgICB9fX0lJSAgICAgICAgICAgIApmbG93Y2hhcnQgTFI7CnN1YmdyYXBoIGV4ZWN1dG9yMDsKZXhlY3V0b3IwL3BlYS0wW2ppbmFodWI6Ly9QUUxpdGVJbmRleGVyL2xhdGVzdF06OjpQRUE7CmVuZDsKZ2F0ZXdheXN0YXJ0W2dhdGV3YXldOjo6R0FURVdBWSAtLT4gZXhlY3V0b3IwOjo6UE9EOwpleGVjdXRvcjA6OjpQT0QgLS0+IGdhdGV3YXllbmRbZ2F0ZXdheV06OjpHQVRFV0FZOwpjbGFzc0RlZiBJTlNQRUNUIHN0cm9rZTojRjI5QzlGCmNsYXNzRGVmIEpPSU5fSU5TUEVDVCBzdHJva2U6I0YyOUM5RgpjbGFzc0RlZiBHQVRFV0FZIGZpbGw6bm9uZSxjb2xvcjojMDAwLHN0cm9rZTpub25lCmNsYXNzRGVmIElOU1BFQ1RfQVVYX1BBU1Mgc3Ryb2tlLWRhc2hhcnJheTogMiAyCmNsYXNzRGVmIEhFQURUQUlMIGZpbGw6IzMyQzhDRDFECgpjbGFzc0RlZiBFWFRFUk5BTCBmaWxsOiNmZmYsc3Ryb2tlOiMzMkM4Q0Q=\\\"/>\"\n            ],\n            \"text/plain\": [\n              \"<IPython.core.display.Image object>\"\n            ]\n          },\n          \"metadata\": {}\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"# clear the workspace folder\\n\",\n        \"!rm -rf workspace/*\\n\",\n        \"\\n\",\n        \"with f:\\n\",\n        \"    f.index(inputs=docs, show_progress=True)\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        },\n        \"id\": \"stLXVMxPImS9\",\n        \"outputId\": \"40e08128-e6a1-48c0-f61c-2bc14b1768a8\"\n      },\n      \"execution_count\": null,\n      \"outputs\": [\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"text/html\": [\n              \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\"></pre>\\n\"\n            ],\n            \"text/plain\": [\n              \"\"\n            ]\n          },\n          \"metadata\": {}\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\r                                                                                                    \\r\\u001b[32m⠋\\u001b[0m 0/2 waiting \\u001b[33mexecutor0 gateway\\u001b[0m to be ready...\\u001b[35m        gateway@2181[D]:setting up sockets...\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2181[D]:control over \\u001b[33mipc:///tmp/tmp8ow78_ah\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2181[D]:input 0.0.0.0:\\u001b[33m39731\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2181[D]:input \\u001b[33mtcp://0.0.0.0:39731\\u001b[0m (ROUTER_BIND) output \\u001b[33mtcp://0.0.0.0:56489\\u001b[0m (ROUTER_BIND) control over \\u001b[33mipc:///tmp/tmp8ow78_ah\\u001b[0m (PAIR_BIND)\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]:ready and listening\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:setting up sockets...\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:control over \\u001b[33mtcp://0.0.0.0:43519\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:input 0.0.0.0:\\u001b[33m36279\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:input \\u001b[33mtcp://0.0.0.0:36279\\u001b[0m (ROUTER_BIND) output \\u001b[33mtcp://0.0.0.0:37367\\u001b[0m (ROUTER_BIND) control over \\u001b[33mtcp://0.0.0.0:43519\\u001b[0m (PAIR_BIND)\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:recv ControlRequest (STATUS)  from \\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:skip executor: not data request\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:ready and listening\\u001b[0m\\n\",\n            \"           Flow@2018[I]:\\u001b[32m🎉 Flow is ready to use!\\u001b[0m\\n\",\n            \"\\t🔗 Protocol: \\t\\t\\u001b[1mGRPC\\u001b[0m\\n\",\n            \"\\t🏠 Local access:\\t\\u001b[4m\\u001b[36m0.0.0.0:46359\\u001b[0m\\n\",\n            \"\\t🔒 Private network:\\t\\u001b[4m\\u001b[36m172.28.0.2:46359\\u001b[0m\\n\",\n            \"\\t🌐 Public address:\\t\\u001b[4m\\u001b[36m34.125.207.64:46359\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m           Flow@2018[D]:2 Pods (i.e. 2 Peas) are running in this Flow\\u001b[0m\\n\",\n            \"\\u001b[35m     GRPCClient@2018[D]:connected to 0.0.0.0:46359\\u001b[0m\\n\",\n            \"\\u001b[32m⠙\\u001b[0m Working... \\u001b[32m━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m estimating... \\u001b[35m        gateway@2181[D]:output 0.0.0.0:\\u001b[33m36279\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (38d2d3cbbae9459792eb981f83dd58a5)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠸\\u001b[0m Working... \\u001b[32m━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m estimating... \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.225 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:output 0.0.0.0:\\u001b[33m39731\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (8cba8c91bd3e4ec8a1ddbd5f063862b4)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠴\\u001b[0m Working... \\u001b[32m━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m 10% ETA: 4 seconds \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.438 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (aa7999aaa6af4027a10419f50ac5cb1e)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠦\\u001b[0m Working... \\u001b[32m━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m 10% ETA: 4 seconds \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.535 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (4bbd37454baf4fe881c2fb9597f7a080)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠧\\u001b[0m Working... \\u001b[32m━━━━━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m 10% ETA: 6 seconds \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.652 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (c37bff10ea61478f852a00bc1e215f2d)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.742 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\r                                                                                                    \\r\\u001b[32m⠇\\u001b[0m Working... \\u001b[32m━━━━━━━━━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m 20% ETA: 3 seconds \\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (3cea32c392c64ce9990d0d7b5a990a22)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.841 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\r                                                                                                    \\r\\u001b[32m⠏\\u001b[0m Working... \\u001b[32m━━━━━━━━━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:00\\u001b[0m 20% ETA: 3 seconds \\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (33d50be993214f6d938f4e37ea40a1b3)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:54.958 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (855c73a74fe64d5987245f1bbbbfd70d)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠋\\u001b[0m Working... \\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:01\\u001b[0m 40% ETA: 1 second \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:55.049 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (2061b4bdaee043bfb278cbabfcf11628)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠙\\u001b[0m Working... \\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:01\\u001b[0m 40% ETA: 1 second \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:55.181 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2180[D]:recv DataRequest (/index) - (dcb0cd00293843908f66bedfe0a3c7c2)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[32m⠹\\u001b[0m Working... \\u001b[32m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m\\u001b[32m╸\\u001b[0m\\u001b[2m\\u001b[32m━━━━\\u001b[0m \\u001b[36m0:00:01\\u001b[0m 70% ETA: 0 seconds \"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:55.270 | DEBUG    | pqlite.container:insert:227 - => 100 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[32m⠼\\u001b[0m       DONE \\u001b[33m━━━━\\u001b[0m\\u001b[33m╸\\u001b[0m\\u001b[2m\\u001b[33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\u001b[0m \\u001b[36m0:00:01\\u001b[0m 100% ETA: 0 seconds \\u001b[K44 steps done in 1 second\\n\",\n            \"\\u001b[35m        gateway@2018[D]:waiting for ready or shutdown signal from runtime\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Cancel runtime\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Wait to shutdown\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2181[D]:#sent: 10 #recv: 10 sent_size: 59.4 MB recv_size: 59.4 MB\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2181[D]:Received message is empty.\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2181[D]: Process terminated\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]:terminated\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:waiting for ready or shutdown signal from runtime\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Cancel runtime\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:Sending TERMINATE command for the 1th time\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:recv ControlRequest (TERMINATE)  from \\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:Handled #0 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:Handled #0 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:Handled #1 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:Handled #0 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]:#sent: 12 #recv: 12 sent_size: 59.4 MB recv_size: 59.4 MB\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Wait to shutdown\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2180[D]: Process terminated\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:terminated\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m           Flow@2018[D]:Flow is closed!\\u001b[0m\\n\"\n          ]\n        }\n      ]\n    },\n    {\n      \"cell_type\": \"code\",\n      \"source\": [\n        \"with f:\\n\",\n        \"    resp = f.search(inputs=query, \\n\",\n        \"                    return_results=True, \\n\",\n        \"                    parameters={\\n\",\n        \"                        'filter': {\\n\",\n        \"                            'year': {'$lte': before_year},\\n\",\n        \"                            'masterCategory': {'$eq': category},\\n\",\n        \"                            'baseColour': {'$eq': color}\\n\",\n        \"                        },\\n\",\n        \"                        'limit': 5\\n\",\n        \"                    })\\n\",\n        \"\\n\",\n        \"for m in resp[0].docs[0].matches:\\n\",\n        \"    m.set_image_blob_channel_axis(0, -1).set_image_blob_inv_normalization()\\n\",\n        \"\\n\",\n        \"resp[0].docs[0].matches.plot_image_sprites()\"\n      ],\n      \"metadata\": {\n        \"colab\": {\n          \"base_uri\": \"https://localhost:8080/\",\n          \"height\": 1000\n        },\n        \"id\": \"r16T6XolI6-W\",\n        \"outputId\": \"bc972718-71e5-4d9c-a6dd-79dc5ce331de\"\n      },\n      \"execution_count\": null,\n      \"outputs\": [\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\r                                                                                                    \\r\\u001b[32m⠋\\u001b[0m 0/2 waiting \\u001b[33mexecutor0 gateway\\u001b[0m to be ready...\\u001b[35m        gateway@2321[D]:setting up sockets...\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]:control over \\u001b[33mipc:///tmp/tmpm6f05h3z\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]:input 0.0.0.0:\\u001b[33m49287\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]:input \\u001b[33mtcp://0.0.0.0:49287\\u001b[0m (ROUTER_BIND) output \\u001b[33mtcp://0.0.0.0:39547\\u001b[0m (ROUTER_BIND) control over \\u001b[33mipc:///tmp/tmpm6f05h3z\\u001b[0m (PAIR_BIND)\\u001b[0m\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:06:59.485 | INFO     | pqlite.index:_rebuild_index:364 - Rebuild the index of cell-0 (1000 docs)...\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m        gateway@2018[D]:ready and listening\\u001b[0m\\n\",\n            \"\\u001b[32m⠇\\u001b[0m 1/2 waiting \\u001b[33mexecutor0\\u001b[0m to be ready...\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stderr\",\n          \"text\": [\n            \"2021-12-16 03:07:00.323 | DEBUG    | pqlite.container:insert:227 - => 1000 new docs added\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"stream\",\n          \"name\": \"stdout\",\n          \"text\": [\n            \"\\u001b[35m      executor0@2320[D]:setting up sockets...\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:control over \\u001b[33mtcp://0.0.0.0:43519\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:input 0.0.0.0:\\u001b[33m36279\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:input \\u001b[33mtcp://0.0.0.0:36279\\u001b[0m (ROUTER_BIND) output \\u001b[33mtcp://0.0.0.0:37367\\u001b[0m (ROUTER_BIND) control over \\u001b[33mtcp://0.0.0.0:43519\\u001b[0m (PAIR_BIND)\\u001b[0m\\n\",\n            \"\\u001b[32m⠏\\u001b[0m 1/2 waiting \\u001b[33mexecutor0\\u001b[0m to be ready...\\u001b[35m      executor0@2320[D]:recv ControlRequest (STATUS)  from \\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:skip executor: not data request\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:ready and listening\\u001b[0m\\n\",\n            \"           Flow@2018[I]:\\u001b[32m🎉 Flow is ready to use!\\u001b[0m\\n\",\n            \"\\t🔗 Protocol: \\t\\t\\u001b[1mGRPC\\u001b[0m\\n\",\n            \"\\t🏠 Local access:\\t\\u001b[4m\\u001b[36m0.0.0.0:55429\\u001b[0m\\n\",\n            \"\\t🔒 Private network:\\t\\u001b[4m\\u001b[36m172.28.0.2:55429\\u001b[0m\\n\",\n            \"\\t🌐 Public address:\\t\\u001b[4m\\u001b[36m34.125.207.64:55429\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m           Flow@2018[D]:2 Pods (i.e. 2 Peas) are running in this Flow\\u001b[0m\\n\",\n            \"\\u001b[35m     GRPCClient@2018[D]:connected to 0.0.0.0:55429\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]:output 0.0.0.0:\\u001b[33m36279\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:recv DataRequest (/search) - (4e23cef8b57b4504b695501df7daf8ba)  from gateway\\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:output 0.0.0.0:\\u001b[33m49287\\u001b[0m\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]:waiting for ready or shutdown signal from runtime\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Cancel runtime\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Wait to shutdown\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]:#sent: 1 #recv: 1 sent_size: 363.9 KB recv_size: 363.9 KB\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]:Received message is empty.\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2321[D]: Process terminated\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]:terminated\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m        gateway@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:waiting for ready or shutdown signal from runtime\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Cancel runtime\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:Sending TERMINATE command for the 1th time\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:recv ControlRequest (TERMINATE)  from \\u001b[32m▸\\u001b[0mexecutor0/ZEDRuntime\\u001b[32m▸\\u001b[0m⚐\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:Handled #0 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:Handled #0 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:Handled #1 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Wait to shutdown\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:Handled #0 during flush of socket\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]:#sent: 3 #recv: 3 sent_size: 366.4 KB recv_size: 364.1 KB\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2320[D]: Process terminated\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]:terminated\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Joining the process\\u001b[0m\\n\",\n            \"\\u001b[35m      executor0@2018[D]: Successfully joined the process\\u001b[0m\\n\",\n            \"\\u001b[35m           Flow@2018[D]:Flow is closed!\\u001b[0m\\n\"\n          ]\n        },\n        {\n          \"output_type\": \"display_data\",\n          \"data\": {\n            \"image/png\": \"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\\n\",\n            \"text/plain\": [\n              \"<Figure size 432x288 with 1 Axes>\"\n            ]\n          },\n          \"metadata\": {}\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\n    \"setuptools>=42\",\n    \"wheel\",\n    \"cython\",\n    \"numpy>=1.10.0\",\n    \"pybind11>=2.9.0\",\n]\n\nbuild-backend = \"setuptools.build_meta\"\n\n"
  },
  {
    "path": "requirements.txt",
    "content": "click\ncython\ndocarray>=0.13.16,<0.30.0\ndocarray[common]>=0.13.16,<0.30.0\nfilesplit\nloguru\nnumpy\nrocksdict>=0.3.9\nscikit-learn\n"
  },
  {
    "path": "scripts/black.sh",
    "content": "#!/bin/bash\npip install black==22.3.0\narrVar=()\necho we ignore non-*.py files\nexcluded_files=(\n   docs/conf.py\n)\nfor changed_file in $CHANGED_FILES; do\n  if [[ ${changed_file} == *.py ]] && ! [[ \" ${excluded_files[@]} \" =~ \" ${changed_file} \" ]]; then\n    echo checking ${changed_file}\n    arrVar+=(${changed_file})\n  fi\ndone\nif [ ${#arrVar[@]} -ne 0 ]; then\n  black -S --check \"${arrVar[@]}\"\nfi\n"
  },
  {
    "path": "scripts/get-all-test-paths.sh",
    "content": "#!/usr/bin/env bash\n\nset -ex\n\nBATCH_SIZE=5\n\ndeclare -a array1=( \"tests/test_*.py\" )\ndeclare -a array2=( \"tests/docarray/test_*.py\" )\ndeclare -a array3=( \"tests/executor/test_*.py\" )\ndest=( \"${array1[@]}\" \"${array2[@]}\" \"${array3[@]}\" )\n\nprintf '%s\\n' \"${dest[@]}\" | jq -R . | jq -cs .\n"
  },
  {
    "path": "scripts/get-last-release-note.py",
    "content": "## under jina root dir\n# python scripts/get-last-release-note.py\n## result in root/tmp.md\n\nwith open('CHANGELOG.md') as fp:\n    n = []\n    for v in fp:\n        if v.startswith('## Release Note'):\n            n.clear()\n        n.append(v)\n\nwith open('tmp.md', 'w') as fp:\n    fp.writelines(n)\n"
  },
  {
    "path": "scripts/release.sh",
    "content": "#!/usr/bin/env bash\n\n# Requirements\n# brew install hub\n# npm install -g git-release-notes\n# pip install twine wheel\n\nset -ex\n\nINIT_FILE='annlite/__init__.py'\nVER_TAG='__version__ = '\nRELEASENOTE='./node_modules/.bin/git-release-notes'\n\nfunction escape_slashes {\n    sed 's/\\//\\\\\\//g'\n}\n\n\nfunction update_ver_line {\n    local OLD_LINE_PATTERN=$1\n    local NEW_LINE=$2\n    local FILE=$3\n\n    local NEW=$(echo \"${NEW_LINE}\" | escape_slashes)\n    if [ \"$(uname)\" == \"Darwin\" ]; then\n      sed -i '' '/'\"${OLD_LINE_PATTERN}\"'/s/.*/'\"${NEW}\"'/' \"${FILE}\";\n    else\n      sed -i '/'\"${OLD_LINE_PATTERN}\"'/s/.*/'\"${NEW}\"'/' \"${FILE}\";\n    fi\n    head -n10 ${FILE}\n}\n\n\nfunction clean_build {\n    rm -rf dist\n    rm -rf annlite/*.so\n    rm -rf *.egg-info\n    rm -rf build\n}\n\n\nfunction pub_pypi {\n    # publish to pypi\n    twine upload dist/*\n    # twine upload wheelhouse/*\n    clean_build\n}\n\nfunction git_commit {\n    git config --local user.email \"dev-bot@jina.ai\"\n    git config --local user.name \"Jina Dev Bot\"\n    git tag \"v$RELEASE_VER\" -m \"$(cat ./CHANGELOG.tmp)\"\n    git add $INIT_FILE ./CHANGELOG.md\n    git commit -m \"chore(version): the next version will be $NEXT_VER\" -m \"build($RELEASE_ACTOR): $RELEASE_REASON\"\n}\n\n\nfunction make_release_note {\n    ${RELEASENOTE} ${LAST_VER}..HEAD .github/release-template.ejs > ./CHANGELOG.tmp\n    head -n10 ./CHANGELOG.tmp\n    printf '\\n%s\\n\\n%s\\n%s\\n\\n%s\\n\\n%s\\n\\n' \"$(cat ./CHANGELOG.md)\" \"<a name=\"release-note-${RELEASE_VER//\\./-}\"></a>\" \"## Release Note (\\`${RELEASE_VER}\\`)\" \"> Release time: $(date +'%Y-%m-%d %H:%M:%S')\" \"$(cat ./CHANGELOG.tmp)\" > ./CHANGELOG.md\n}\n\nBRANCH=$(git rev-parse --abbrev-ref HEAD)\n\nif [[ \"$BRANCH\" != \"main\" ]]; then\n  printf \"You are not at main branch, exit\\n\";\n  exit 1;\nfi\n\nLAST_UPDATE=`git show --no-notes --format=format:\"%H\" $BRANCH | head -n 1`\nLAST_COMMIT=`git show --no-notes --format=format:\"%H\" origin/$BRANCH | head -n 1`\n\nif [ $LAST_COMMIT != $LAST_UPDATE ]; then\n    printf \"Your local $BRANCH is behind the remote master, exit\\n\"\n    exit 1;\nfi\n\n# release the current version\nexport RELEASE_VER=$(sed -n '/^__version__/p' $INIT_FILE | cut -d \\' -f2)\nLAST_VER=$(git tag -l | sort -V | tail -n1)\nprintf \"last version: \\e[1;32m$LAST_VER\\e[0m\\n\"\n\nif [[ $1 == \"final\" ]]; then\n  printf \"this will be a final release: \\e[1;33m$RELEASE_VER\\e[0m\\n\"\n\n  NEXT_VER=$(echo $RELEASE_VER | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf(\"%0*d\", length($NF), ($NF+1)); print}')\n  printf \"bump master version to: \\e[1;32m$NEXT_VER\\e[0m\\n\"\n\n  make_release_note\n  pub_pypi\n\n  VER_TAG_NEXT=$VER_TAG\\'${NEXT_VER}\\'\n  update_ver_line \"$VER_TAG\" \"$VER_TAG_NEXT\" \"$INIT_FILE\"\n\n  RELEASE_REASON=\"$2\"\n  RELEASE_ACTOR=\"$3\"\n  git_commit\nelif [[ $1 == 'rc' ]]; then\n  printf \"this will be a release candidate: \\e[1;33m$RELEASE_VER\\e[0m\\n\"\n  DOT_RELEASE_VER=$(echo $RELEASE_VER | sed \"s/rc/\\./\")\n  NEXT_VER=$(echo $DOT_RELEASE_VER | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf(\"%0*d\", length($NF), ($NF+1)); print}')\n  NEXT_VER=$(echo $NEXT_VER | sed \"s/\\.\\([^.]*\\)$/rc\\1/\")\n  printf \"bump master version to: \\e[1;32m$NEXT_VER\\e[0m, this will be the next version\\n\"\n\n  make_release_note\n  pub_pypi\n\n  RELEASE_REASON=\"$2\"\n  RELEASE_ACTOR=\"$3\"\n  git_commit\nelse\n  # as a prerelease, pypi update only, no back commit etc.\n  COMMITS_SINCE_LAST_VER=$(git rev-list $LAST_VER..HEAD --count)\n  NEXT_VER=$RELEASE_VER\".dev\"$COMMITS_SINCE_LAST_VER\n  printf \"this will be a developmental release: \\e[1;33m$NEXT_VER\\e[0m\\n\"\n\n  pub_pypi\n\nfi\n"
  },
  {
    "path": "scripts/update-version.sh",
    "content": "#!/usr/bin/env bash\n\n# Requirements\n# brew install hub\n# npm install -g git-release-notes\n# pip install twine wheel\n\nset -ex\n\nINIT_FILE='annlite/__init__.py'\nVER_TAG='__version__ = '\nRELEASENOTE='./node_modules/.bin/git-release-notes'\n\nfunction escape_slashes {\n    sed 's/\\//\\\\\\//g'\n}\n\nfunction update_ver_line {\n    local OLD_LINE_PATTERN=$1\n    local NEW_LINE=$2\n    local FILE=$3\n\n    local NEW=$(echo \"${NEW_LINE}\" | escape_slashes)\n    if [ \"$(uname)\" == \"Darwin\" ]; then\n      sed -i '' '/'\"${OLD_LINE_PATTERN}\"'/s/.*/'\"${NEW}\"'/' \"${FILE}\";\n    else\n      sed -i '/'\"${OLD_LINE_PATTERN}\"'/s/.*/'\"${NEW}\"'/' \"${FILE}\";\n    fi\n    head -n10 ${FILE}\n}\n\n\nBRANCH=$(git rev-parse --abbrev-ref HEAD)\n\nif [[ \"$BRANCH\" != \"main\" ]]; then\n  printf \"You are not at main branch, exit\\n\";\n  exit 1;\nfi\n\nLAST_UPDATE=`git show --no-notes --format=format:\"%H\" $BRANCH | head -n 1`\nLAST_COMMIT=`git show --no-notes --format=format:\"%H\" origin/$BRANCH | head -n 1`\n\nif [ $LAST_COMMIT != $LAST_UPDATE ]; then\n    printf \"Your local $BRANCH is behind the remote master, exit\\n\"\n    exit 1;\nfi\n\n# update the current version\nexport RELEASE_VER=$(sed -n '/^__version__/p' $INIT_FILE | cut -d \\' -f2)\nLAST_VER=$(git tag -l | sort -V | tail -n1)\nprintf \"last version: \\e[1;32m$LAST_VER\\e[0m\\n\"\n\nif [[ $1 == \"final\" ]]; then\n  printf \"this will be a final release: \\e[1;33m$RELEASE_VER\\e[0m\\n\"\n\n  NEXT_VER=$(echo $RELEASE_VER | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf(\"%0*d\", length($NF), ($NF+1)); print}')\n  printf \"bump master version to: \\e[1;32m$NEXT_VER\\e[0m\\n\"\n\n  VER_TAG_NEXT=$VER_TAG\\'${NEXT_VER}\\'\n  update_ver_line \"$VER_TAG\" \"$VER_TAG_NEXT\" \"$INIT_FILE\"\n\nelif [[ $1 == 'rc' ]]; then\n  printf \"this will be a release candidate: \\e[1;33m$RELEASE_VER\\e[0m\\n\"\n  DOT_RELEASE_VER=$(echo $RELEASE_VER | sed \"s/rc/\\./\")\n  NEXT_VER=$(echo $DOT_RELEASE_VER | awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{$NF=sprintf(\"%0*d\", length($NF), ($NF+1)); print}')\n  NEXT_VER=$(echo $NEXT_VER | sed \"s/\\.\\([^.]*\\)$/rc\\1/\")\n  printf \"bump master version to: \\e[1;32m$NEXT_VER\\e[0m, this will be the next version\\n\"\n\n  VER_TAG_NEXT=$VER_TAG\\'${NEXT_VER}\\'\n  update_ver_line \"$VER_TAG\" \"$VER_TAG_NEXT\" \"$INIT_FILE\"\n  \nelse\n  # as a prerelease, pypi update only, no back commit etc.\n  COMMITS_SINCE_LAST_VER=$(git rev-list $LAST_VER..HEAD --count)\n  NEXT_VER=$RELEASE_VER\".dev\"$COMMITS_SINCE_LAST_VER\n  printf \"this will be a developmental release: \\e[1;33m$NEXT_VER\\e[0m\\n\"\n\n  VER_TAG_NEXT=$VER_TAG\\'${NEXT_VER}\\'\n  update_ver_line \"$VER_TAG\" \"$VER_TAG_NEXT\" \"$INIT_FILE\"\n\nfi\n"
  },
  {
    "path": "setup.py",
    "content": "import os\nimport platform\nimport sys\nfrom distutils.sysconfig import get_python_inc\n\nimport numpy as np\nimport pybind11\nimport setuptools\nfrom Cython.Build import cythonize\nfrom setuptools import Extension, find_packages, setup\nfrom setuptools.command.build_ext import build_ext\n\nif sys.version_info < (3, 7, 0):\n    raise OSError(f'annlite requires Python 3.7+, but yours is {sys.version}')\n\ninclude_dirs = [pybind11.get_include(), np.get_include()]\n\nlibraries = []\nextra_objects = []\n\ntry:\n    pkg_name = 'annlite'\n    libinfo_py = os.path.join(pkg_name, '__init__.py')\n    libinfo_content = open(libinfo_py, 'r', encoding='utf8').readlines()\n    version_line = [l.strip() for l in libinfo_content if l.startswith('__version__')][\n        0\n    ]\n    exec(version_line)  # produce __version__\nexcept FileNotFoundError:\n    __version__ = '0.0.0'\n\ntry:\n    with open('README.md', encoding='utf8') as fp:\n        _long_description = fp.read()\nexcept FileNotFoundError:\n    _long_description = ''\n\ntry:\n    with open('requirements.txt') as f:\n        base_deps = f.read().splitlines()\n\n    # remove blank lines and comments\n    base_deps = [\n        x.strip()\n        for x in base_deps\n        if ((x.strip()[0] != '#') and (len(x.strip()) > 3) and '-e git://' not in x)\n    ]\nexcept FileNotFoundError:\n    base_deps = []\n\nCOMPILER_DIRECTIVES = {\n    'language_level': -3,\n    'embedsignature': True,\n    'annotation_typing': False,\n}\n\next_modules = [\n    Extension(\n        'annlite.hnsw_bind',\n        ['./bindings/hnsw_bindings.cpp'],\n        include_dirs=include_dirs + ['./include/hnswlib'],\n        libraries=libraries,\n        language='c++',\n        extra_objects=extra_objects,\n    )\n] + cythonize(\n    [\n        Extension(\n            'annlite.pq_bind',\n            ['./bindings/pq_bindings.pyx'],\n            include_dirs=include_dirs + [get_python_inc(plat_specific=True)],\n            libraries=libraries,\n            language='c++',\n            extra_objects=extra_objects,\n        ),\n    ],\n    compiler_directives=COMPILER_DIRECTIVES,\n)\n#\n# ext_modules = cythonize([\n#                 Extension(\n#                     'pqlite.pq_bind',\n#                     ['./bindings/pq_bindings.pyx'],\n#                     include_dirs=include_dirs + [get_python_inc(plat_specific=True)],\n#                     libraries=libraries,\n#                     language='c++',\n#                     extra_objects=extra_objects,\n#                 ),\n#             ], compiler_directives=COMPILER_DIRECTIVES)\n\n# As of Python 3.6, CCompiler has a `has_flag` method.\n# cf http://bugs.python.org/issue26689\ndef has_flag(compiler, flagname):\n    \"\"\"Return a boolean indicating whether a flag name is supported on\n    the specified compiler.\n    \"\"\"\n    import tempfile\n\n    with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f:\n        f.write('int main (int argc, char **argv) { return 0; }')\n        try:\n            compiler.compile([f.name], extra_postargs=[flagname])\n        except setuptools.distutils.errors.CompileError:\n            return False\n    return True\n\n\ndef cpp_flag(compiler):\n    \"\"\"Return the -std=c++[11/14] compiler flag.\n    The c++14 is prefered over c++11 (when it is available).\n    \"\"\"\n    if has_flag(compiler, '-std=c++14'):\n        return '-std=c++14'\n    elif has_flag(compiler, '-std=c++11'):\n        return '-std=c++11'\n    else:\n        raise RuntimeError(\n            'Unsupported compiler -- at least C++11 support ' 'is needed!'\n        )\n\n\nclass BuildExt(build_ext):\n    \"\"\"A custom build extension for adding compiler-specific options.\"\"\"\n\n    c_opts = {\n        'msvc': ['/EHsc', '/openmp', '/O2'],\n        'unix': ['-O3', '-march=native'],  # , '-w'\n    }\n    link_opts = {\n        'unix': [],\n        'msvc': [],\n    }\n\n    if sys.platform == 'darwin':\n        if platform.processor() in ('arm64', 'arm'):\n            c_opts['unix'].remove('-march=native')\n            # thanks to @https://github.com/drkeoni\n            # https://github.com/nmslib/nmslib/issues/476#issuecomment-876094529\n            c_opts['unix'].append('-mcpu=apple-a14')\n        c_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7']\n        link_opts['unix'] += ['-stdlib=libc++', '-mmacosx-version-min=10.7']\n    else:\n        c_opts['unix'].append('-fopenmp')\n        link_opts['unix'].extend(['-fopenmp', '-pthread'])\n\n    def build_extensions(self):\n        ct = self.compiler.compiler_type\n        opts = self.c_opts.get(ct, [])\n        if ct == 'unix':\n            opts.append('-DVERSION_INFO=\"%s\"' % self.distribution.get_version())\n            opts.append(cpp_flag(self.compiler))\n            # if has_flag(self.compiler, '-fvisibility=hidden'):\n            #     opts.append('-fvisibility=hidden')\n        elif ct == 'msvc':\n            opts.append('/DVERSION_INFO=\\\\\"%s\\\\\"' % self.distribution.get_version())\n\n        for ext in self.extensions:\n            ext.extra_compile_args.extend(opts)\n            ext.extra_link_args.extend(self.link_opts.get(ct, []))\n\n        build_ext.build_extensions(self)\n\n\nextras = {}\nextras['test'] = [\n    'pytest',\n    'black',\n    'pytest-timeout',\n    'pytest-mock',\n    'pytest-cov',\n    'pytest-repeat',\n    'pytest-reraise',\n    'pytest-custom_exit_code',\n    'jina',\n]\n\n# for e in ext_modules:\n#     e.cython_directives = COMPILER_DIRECTIVES\n\nsetup(\n    name='annlite',\n    version=__version__,\n    description='Fast and Light Approximate Nearest Neighbor Search Library integrated with the Jina Ecosystem',\n    long_description=_long_description,\n    long_description_content_type='text/markdown',\n    author='Jina AI',\n    author_email='team@jina.ai',\n    url='https://github.com/jina-ai/annlite',\n    download_url='https://github.com/jina-ai/pqlite/tags',\n    license='Apache License 2.0',\n    extras_require=extras,\n    ext_modules=ext_modules,\n    cmdclass={'build_ext': BuildExt},\n    package_data={'bindings': ['*.pyx', '*.pxd', '*.pxi']},\n    install_requires=base_deps,\n    setup_requires=['setuptools>=18.0', 'wheel', 'cython', 'build'],\n    classifiers=[\n        'Intended Audience :: Developers',\n        'Intended Audience :: Science/Research',\n        'License :: OSI Approved :: Apache Software License',\n        'Operating System :: OS Independent',\n        'Programming Language :: Python :: 3.7',\n        'Programming Language :: Python :: 3.8',\n        'Programming Language :: Python :: 3.9',\n        'Topic :: Scientific/Engineering :: Artificial Intelligence',\n    ],\n    python_requires='>=3.7',\n    packages=find_packages(\n        exclude=[\n            '*.tests',\n            '*.tests.*',\n            'tests.*',\n            'tests',\n            'test',\n            'docs',\n            'src',\n            'executor',\n        ]\n    ),\n    zip_safe=False,\n    keywords='product-quantization approximate-nearest-neighbor hnsw',\n)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/conftest.py",
    "content": "import tempfile\n\nimport numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\n\n@pytest.fixture(scope='session')\ndef docs():\n    return DocumentArray(\n        [\n            Document(id='doc1', embedding=np.array([1, 0, 0, 0])),\n            Document(id='doc2', embedding=np.array([0, 1, 0, 0])),\n            Document(id='doc3', embedding=np.array([0, 0, 1, 0])),\n            Document(id='doc4', embedding=np.array([0, 0, 0, 1])),\n            Document(id='doc5', embedding=np.array([1, 0, 1, 0])),\n            Document(id='doc6', embedding=np.array([0, 1, 0, 1])),\n        ]\n    )\n\n\n@pytest.fixture(scope='session')\ndef update_docs():\n    return DocumentArray(\n        [\n            Document(id='doc1', embedding=np.array([0, 0, 0, 1])),\n        ]\n    )\n\n\n@pytest.fixture(autouse=True)\ndef tmpfile(tmpdir):\n    tmpfile = f'annlite_test_{next(tempfile._get_candidate_names())}.db'\n    return tmpdir / tmpfile\n\n\n@pytest.fixture(autouse=True)\ndef test_disable_telemetry(monkeypatch):\n    monkeypatch.setenv('JINA_OPTOUT_TELEMETRY', 'True')\n"
  },
  {
    "path": "tests/docarray/__init__.py",
    "content": ""
  },
  {
    "path": "tests/docarray/test_add.py",
    "content": "import pytest\nfrom docarray import DocumentArray\n\n\ndef test_add(docs):\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 4,\n        },\n    )\n\n    annlite_doc.extend(docs)\n\n    assert len(annlite_doc) == len(annlite_doc[:, 'embedding'])\n    assert len(annlite_doc[:, 'embedding']) == 6\n\n\ndef test_add_conflict_id(docs, update_docs):\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 4,\n        },\n    )\n\n    annlite_doc.extend(docs)\n\n    from sqlite3 import IntegrityError\n\n    with pytest.raises(IntegrityError):\n        annlite_doc.extend(update_docs)\n"
  },
  {
    "path": "tests/docarray/test_del.py",
    "content": "import pytest\nfrom docarray import Document, DocumentArray\n\n\n@pytest.mark.parametrize('deleted_elmnts', [[0, 1], ['r0', 'r1']])\ndef test_delete_success(deleted_elmnts):\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 3,\n        },\n    )\n\n    with annlite_doc:\n        annlite_doc.extend(\n            [\n                Document(id='r0', embedding=[0, 0, 0]),\n                Document(id='r1', embedding=[1, 1, 1]),\n                Document(id='r2', embedding=[2, 2, 2]),\n                Document(id='r3', embedding=[3, 3, 3]),\n                Document(id='r4', embedding=[4, 4, 4]),\n                Document(id='r5', embedding=[5, 5, 5]),\n                Document(id='r6', embedding=[6, 6, 6]),\n                Document(id='r7', embedding=[7, 7, 7]),\n            ]\n        )\n\n    expected_ids_after_del = ['r2', 'r3', 'r4', 'r5', 'r6', 'r7']\n\n    with annlite_doc:\n        del annlite_doc[deleted_elmnts]\n\n    assert len(annlite_doc._offset2ids.ids) == 6\n    assert len(annlite_doc[:, 'embedding']) == 6\n\n    for id in expected_ids_after_del:\n        assert id == annlite_doc[id].id\n\n\n@pytest.mark.parametrize('expected_failed_deleted_elmnts', [['r2', 'r3']])\ndef test_delete_not_found(expected_failed_deleted_elmnts):\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 3,\n        },\n    )\n    with annlite_doc:\n        annlite_doc.extend(\n            [\n                Document(id='r0', embedding=[0, 0, 0]),\n                Document(id='r1', embedding=[1, 1, 1]),\n            ]\n        )\n\n    with pytest.raises(ValueError):\n        with annlite_doc:\n            for deleted_elmnts in expected_failed_deleted_elmnts:\n                del annlite_doc[deleted_elmnts]\n"
  },
  {
    "path": "tests/docarray/test_find.py",
    "content": "import numpy as np\nfrom docarray import Document, DocumentArray\n\n\ndef test_find():\n    nrof_docs = 1000\n    num_candidates = 100\n\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 3,\n        },\n    )\n\n    with annlite_doc:\n        annlite_doc.extend(\n            [\n                Document(id=f'r{i}', embedding=np.ones((3,)) * i)\n                for i in range(nrof_docs)\n            ],\n        )\n\n    np_query = np.array([2, 1, 3])\n\n    annlite_doc.find(np_query, limit=10, num_candidates=num_candidates)\n"
  },
  {
    "path": "tests/docarray/test_get.py",
    "content": "import numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\n\n@pytest.mark.parametrize('nrof_docs', [10, 100, 10_000, 10_100, 20_000, 20_100])\ndef test_success_get_bulk_data(nrof_docs):\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 3,\n        },\n    )\n\n    with annlite_doc:\n        annlite_doc.extend(\n            [\n                Document(id=f'r{i}', embedding=np.ones((3,)) * i)\n                for i in range(nrof_docs)\n            ]\n        )\n\n    assert len(annlite_doc[:, 'id']) == nrof_docs\n\n\ndef test_error_get_bulk_data_id_not_exist():\n    nrof_docs = 10\n\n    annlite_doc = DocumentArray(\n        storage='annlite',\n        config={\n            'n_dim': 3,\n        },\n    )\n\n    with annlite_doc:\n        annlite_doc.extend(\n            [\n                Document(id=f'r{i}', embedding=np.ones((3,)) * i)\n                for i in range(nrof_docs)\n            ]\n        )\n\n    with pytest.raises(KeyError) as e:\n        annlite_doc[['r1', 'r11', 'r21'], 'id']\n"
  },
  {
    "path": "tests/docarray/test_save_load.py",
    "content": "import numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\n\ndef test_save_load(tmpfile):\n    N = 100\n\n    save_da = DocumentArray(\n        storage='annlite', config={'n_dim': 768, 'data_path': tmpfile}\n    )\n    for i in range(N):\n        save_da.append(Document(id=str(i), embedding=np.random.rand(768)))\n\n    # need release the resource\n    save_da._annlite.close()\n\n    load_da = DocumentArray(\n        storage='annlite', config={'n_dim': 768, 'data_path': tmpfile}\n    )\n    assert len(load_da) == N\n\n    for i in range(N, N + N):\n        load_da.append(Document(id=str(i), embedding=np.random.rand(768)))\n    assert len(load_da) == N + N\n"
  },
  {
    "path": "tests/executor/__init__.py",
    "content": ""
  },
  {
    "path": "tests/executor/test_executor.py",
    "content": "import os\nimport time\nimport uuid\nfrom unittest.mock import patch\n\nimport hubble\nimport numpy as np\nimport pytest\nfrom jina import Document, DocumentArray, Executor, Flow\n\nfrom annlite.executor import AnnLiteIndexer\n\nN = 1000  # number of data points\nNt = 2000\nNu = 999  # number of data update\nNq = 10\nD = 128  # dimentionality / number of features\n\ntoken = 'ed17d158d95d3f53f60eed445d783c80'\n\n\ndef gen_docs(num):\n    res = DocumentArray()\n    k = np.random.random((num, D)).astype(np.float32)\n    for i in range(num):\n        doc = Document(id=f'{i}', embedding=k[i])\n        res.append(doc)\n    return res\n\n\ndef docs_with_tags(N):\n    prices = [10.0, 25.0, 50.0, 100.0]\n    categories = ['comics', 'movies', 'audiobook']\n    X = np.random.random((N, D)).astype(np.float32)\n    docs = [\n        Document(\n            id=f'{i}',\n            embedding=X[i],\n            tags={\n                'price': np.random.choice(prices),\n                'category': np.random.choice(categories),\n            },\n        )\n        for i in range(N)\n    ]\n    da = DocumentArray(docs)\n\n    return da\n\n\ndef delete_artifact(tmpname):\n    client = hubble.Client(token=token, max_retries=None, jsonify=True)\n    art_list = client.list_artifacts(filter={'metaData.name': tmpname})\n    for art in art_list['data']:\n        client.delete_artifact(id=art['_id'])\n\n\ndef test_index(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n    )\n    with f:\n        result = f.post(on='/index', inputs=docs, return_results=True)\n        assert len(result) == N\n\n\ndef test_update(tmpfile):\n    docs = gen_docs(N)\n    docs_update = gen_docs(Nu)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n\n        time.sleep(2)\n\n        update_res = f.post(on='/update', inputs=docs_update, return_results=True)\n        assert len(update_res) == Nu\n\n        status = f.post(on='/status', return_results=True)[0]\n\n        assert int(status.tags['total_docs']) == N\n        assert int(status.tags['index_size']) == N\n\n\ndef test_search(tmpfile):\n    docs = gen_docs(N)\n    docs_query = gen_docs(Nq)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n\n        time.sleep(2)\n\n        query_res = f.post(on='/search', inputs=docs_query, return_results=True)\n        assert len(query_res) == Nq\n\n        for i in range(len(query_res[0].matches) - 1):\n            assert (\n                query_res[0].matches[i].scores['cosine'].value\n                <= query_res[0].matches[i + 1].scores['cosine'].value\n            )\n\n\n@pytest.mark.parametrize(\n    'columns',\n    [[('price', 'float'), ('category', 'str')], {'price': 'float', 'category': 'str'}],\n)\ndef test_search_with_filtering(tmpfile, columns):\n    docs = docs_with_tags(N)\n    docs_query = gen_docs(1)\n\n    f = Flow().add(\n        uses=AnnLiteIndexer, uses_with={'dim': D, 'columns': columns}, workspace=tmpfile\n    )\n\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n\n        query_res = f.post(\n            on='/search',\n            inputs=docs_query,\n            return_results=True,\n            parameters={'filter': {'price': {'$lt': 50.0}}, 'include_metadata': True},\n        )\n        assert all([m.tags['price'] < 50 for m in query_res[0].matches])\n\n\ndef test_delete(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'dim': D,\n        },\n        workspace=tmpfile,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n\n        status = f.post(on='/status', return_results=True)[0]\n        assert int(status.tags['total_docs']) == N\n        assert int(status.tags['index_size']) == N\n\n        f.post(on='/delete', parameters={'ids': [d.id for d in docs[:5]]})\n        status = f.post(on='/status', return_results=True)[0]\n        assert int(status.tags['total_docs']) == N - 5\n        assert int(status.tags['index_size']) == N - 5\n\n        docs_query = gen_docs(Nq)\n        query_res = f.post(on='/search', inputs=docs_query, return_results=True)\n\n\ndef test_status(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'dim': D,\n        },\n        workspace=tmpfile,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        status = f.post(on='/status', return_results=True)[0]\n        assert int(status.tags['total_docs']) == N\n        assert int(status.tags['index_size']) == N\n\n\ndef test_clear(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'dim': D,\n        },\n        workspace=tmpfile,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        f.post(on='/clear')\n        status = f.post(on='/status', return_results=True)[0]\n        assert int(status.tags['total_docs']) == 0\n        assert int(status.tags['index_size']) == 0\n\n\ndef test_remote_storage(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=1,\n    )\n    tmpname = uuid.uuid4().hex\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        f.post(on='/backup', parameters={'target_name': tmpname, 'token': token})\n        time.sleep(2)\n\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={'n_dim': D},\n        workspace=tmpfile,\n        shards=1,\n    )\n    with f:\n        f.post(on='/restore', parameters={'source_name': tmpname, 'token': token})\n        status = f.post(on='/status', return_results=True)[0]\n\n    delete_artifact(tmpname)\n    assert int(status.tags['total_docs']) == N\n    assert int(status.tags['index_size']) == N\n\n\ndef test_local_storage(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=1,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        f.post(on='/backup')\n        time.sleep(2)\n\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={'n_dim': D},\n        workspace=tmpfile,\n        shards=1,\n    )\n    with f:\n        f.post(on='/restore')\n        status = f.post(on='/status', return_results=True)[0]\n\n    assert int(status.tags['total_docs']) == N\n    assert int(status.tags['index_size']) == N\n\n\ndef test_remote_storage_with_shards(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=3,\n        polling={\n            '/index': 'ANY',\n            '/search': 'ALL',\n            '/backup': 'ALL',\n            '/status': 'ALL',\n            'backup': 'ALL',\n            'restore': 'ALL',\n        },\n    )\n    tmpname = uuid.uuid4().hex\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        f.post(\n            on='/backup',\n            parameters={'target_name': tmpname, 'token': token},\n        )\n        time.sleep(2)\n\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=3,\n        polling={\n            '/index': 'ANY',\n            '/search': 'ALL',\n            '/backup': 'ALL',\n            '/status': 'ALL',\n            'backup': 'ALL',\n            'restore': 'ALL',\n        },\n    )\n    with f:\n        f.post(on='/restore', parameters={'source_name': tmpname, 'token': token})\n        status = f.post(on='/status', return_results=True)\n\n    delete_artifact(tmpname)\n    total_docs = 0\n    index_size = 0\n    for stat in status:\n        total_docs += stat.tags['total_docs']\n        index_size += stat.tags['index_size']\n    assert total_docs == N\n    assert index_size == N\n\n\ndef test_local_storage_with_shards(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=3,\n        polling={\n            '/index': 'ANY',\n            '/search': 'ALL',\n            '/backup': 'ALL',\n            '/status': 'ALL',\n            'backup': 'ALL',\n            'restore': 'ALL',\n        },\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        f.post(on='/backup')\n        time.sleep(2)\n\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={'n_dim': D},\n        workspace=tmpfile,\n        shards=3,\n        polling={\n            '/index': 'ANY',\n            '/search': 'ALL',\n            '/backup': 'ALL',\n            '/status': 'ALL',\n            'backup': 'ALL',\n            'restore': 'ALL',\n        },\n    )\n    with f:\n        f.post(on='/restore')\n        status = f.post(on='/status', return_results=True)\n\n    total_docs = 0\n    index_size = 0\n    for stat in status:\n        total_docs += stat.tags['total_docs']\n        index_size += stat.tags['index_size']\n    assert total_docs == N\n    assert index_size == N\n\n\ndef test_local_storage_with_delete(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=1,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        f.post(on='/backup')\n        time.sleep(2)\n\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={'n_dim': D},\n        workspace=tmpfile,\n        shards=1,\n    )\n    with f:\n        f.post(on='/restore')\n        f.post(on='/delete', parameters={'ids': ['0']})\n        status = f.post(on='/status', return_results=True)[0]\n\n    assert int(status.tags['total_docs']) == N - 1\n    assert int(status.tags['index_size']) == N - 1\n\n\ndef test_local_storage_delete_update(tmpfile):\n    docs = gen_docs(N)\n    f = Flow().add(\n        uses=AnnLiteIndexer,\n        uses_with={\n            'n_dim': D,\n        },\n        workspace=tmpfile,\n        shards=1,\n    )\n    with f:\n        f.post(on='/index', inputs=docs)\n        time.sleep(2)\n        f.post(on='/backup')\n\n        f.post(on='/delete', parameters={'ids': ['0']})\n        time.sleep(2)\n        f.post(on='/backup')\n\n        new_doc = gen_docs(N)[1:]\n        f.post(on='/update', inputs=new_doc)\n        status = f.post(on='/status', return_results=True)[0]\n\n        assert int(status.tags['total_docs']) == N - 1\n        assert int(status.tags['index_size']) == N - 1\n"
  },
  {
    "path": "tests/test_codec.py",
    "content": "import numpy as np\nimport pytest\n\nfrom annlite.core.codec.pq import PQCodec\n\nn_examples = 1000\nn_features = 128\nn_queries = 5\nn_cells = 10\nn_clusters = 256\nn_subvectors = 32\nd_subvector = int(n_features / n_subvectors)\ntop_k = 100\n\n\n@pytest.fixture\ndef build_data():\n    Xt = np.random.random((n_examples, n_features)).astype(np.float32)\n    return Xt\n\n\n@pytest.fixture\ndef build_pq_codec(build_data):\n    Xt = build_data\n    pq_codec = PQCodec(dim=n_features, n_subvectors=n_subvectors, n_clusters=n_clusters)\n    pq_codec.fit(Xt)\n    return pq_codec\n\n\ndef minibatch_generator(Xtr, batch_size):\n    num = 0\n    pos_begin_batch = 0\n    n_examples = len(Xtr)\n\n    while True:\n        Xtr_batch = Xtr[pos_begin_batch : pos_begin_batch + batch_size]\n        yield Xtr_batch\n\n        num += len(Xtr_batch)\n        pos_begin_batch += batch_size\n\n        if num + batch_size >= n_examples:\n            break\n\n\n@pytest.fixture\ndef build_pq_codec_online(build_data):\n    Xt = build_data\n    pq_codec_minibatch = PQCodec(\n        dim=n_features, n_subvectors=n_subvectors, n_clusters=n_clusters\n    )\n    n_epochs = 3\n    n_batch = 300\n\n    for i in range(n_epochs):\n        minibatch_generator_ = minibatch_generator(Xt, n_batch)\n\n        for batch in minibatch_generator_:\n            pq_codec_minibatch.partial_fit(batch)\n\n    pq_codec_minibatch.build_codebook()\n    return pq_codec_minibatch\n\n\ndef test_partial_and_total_fit_same_codebook_shape(\n    build_pq_codec, build_pq_codec_online\n):\n    pq_codec = build_pq_codec\n    pq_codec_minibatch = build_pq_codec_online\n    assert pq_codec.codebooks.shape == pq_codec_minibatch.codebooks.shape\n"
  },
  {
    "path": "tests/test_crud.py",
    "content": "import random\n\nimport numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\nfrom annlite import AnnLite\n\nN = 1000\nNt = 5\nD = 128\n\n\n@pytest.fixture\ndef annlite_with_data(tmpfile):\n    columns = [('x', float)]\n    index = AnnLite(n_dim=D, columns=columns, data_path=tmpfile)\n\n    X = np.random.random((N, D)).astype(np.float32)\n\n    docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=X[i], tags={'x': random.random()})\n            for i in range(N)\n        ]\n    )\n    index.index(docs)\n    return index\n\n\n@pytest.mark.parametrize('filter', [None, {'x': {'$lt': 0.5}}])\n@pytest.mark.parametrize('limit', [-1, 1, 3, 5])\ndef test_get(annlite_with_data, filter, limit):\n    index = annlite_with_data\n\n    docs = index.get_docs(filter=filter, limit=limit)\n    if limit > 0:\n        assert len(docs) == limit\n    elif limit == -1 and filter is None:\n        assert len(docs) == N\n\n    if filter:\n        for doc in docs:\n            assert doc.tags['x'] < 0.5\n\n\ndef test_update_legal(annlite_with_data):\n    index = annlite_with_data\n\n    updated_X = np.random.random((Nt, D)).astype(np.float32)\n    updated_docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=updated_X[i], tags={'x': random.random()})\n            for i in range(Nt)\n        ]\n    )\n\n    index.update(updated_docs)\n    index.search(updated_docs)\n    for i in range(Nt):\n        np.testing.assert_array_almost_equal(\n            updated_docs[i].embedding, updated_docs[i].matches[0].embedding, decimal=5\n        )\n\n\ndef test_update_illegal(annlite_with_data):\n    index = annlite_with_data\n\n    updated_X = np.random.random((Nt, D)).astype(np.float32)\n    updated_docs = DocumentArray(\n        [\n            Document(\n                id=f'{i}_wrong', embedding=updated_X[i], tags={'x': random.random()}\n            )\n            for i in range(Nt)\n        ]\n    )\n\n    with pytest.raises(Exception):\n        index.update(\n            updated_docs, raise_errors_on_not_found=True, insert_if_not_found=False\n        )\n    with pytest.warns(RuntimeWarning):\n        index.update(\n            updated_docs, raise_errors_on_not_found=False, insert_if_not_found=False\n        )\n    index.update(updated_docs, raise_errors_on_not_found=True, insert_if_not_found=True)\n\n\ndef test_delete_legal(annlite_with_data):\n    index = annlite_with_data\n\n    deleted_docs = DocumentArray([Document(id=f'{i}') for i in range(Nt)])\n\n    index.delete(deleted_docs)\n    assert index.stat['total_docs'] == N - Nt\n\n\ndef test_delete_illegal(annlite_with_data):\n    index = annlite_with_data\n\n    deleted_docs = DocumentArray([Document(id=f'{i}_wrong') for i in range(Nt)])\n\n    with pytest.raises(Exception):\n        index.delete(deleted_docs, raise_errors_on_not_found=True)\n"
  },
  {
    "path": "tests/test_dump.py",
    "content": "import numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\nfrom annlite import AnnLite\n\nnp.random.seed(123456)\n\nD = 50\nN = 1000\n\n\n@pytest.fixture\ndef index_data():\n    index_data = DocumentArray()\n    for i in range(N):\n        index_data.append(Document(id=str(i)))\n    half_embedding = np.random.random((N, D // 2))\n    index_data.embeddings = np.concatenate([half_embedding, half_embedding], axis=1)\n    return index_data\n\n\ndef test_dump_load(tmpfile, index_data):\n    query = index_data[0:1]\n\n    index = AnnLite(D, data_path=tmpfile)\n    index.index(index_data)\n    index.search(query, limit=10)\n    gt = [m.id for m in query['@m']]\n    index.dump()\n    index.close()\n\n    new_index = AnnLite(D, data_path=tmpfile)\n    new_index.search(query, limit=10)\n    new_gt = [m.id for m in query['@m']]\n    assert len(set(gt) & set(new_gt)) / len(gt) == 1.0\n    new_index.close()\n\n    new_index = AnnLite(D, n_components=D // 2, data_path=tmpfile)\n    new_index.search(query, limit=10)\n    new_gt = [m.id for m in query['@m']]\n    assert len(set(gt) & set(new_gt)) / len(gt) > 0.6\n"
  },
  {
    "path": "tests/test_enums.py",
    "content": "import pytest\n\nfrom annlite.enums import Metric\n\n\ndef test_metric():\n    m = Metric.EUCLIDEAN\n\n    assert m.name == 'EUCLIDEAN'\n    assert m.value == 1\n"
  },
  {
    "path": "tests/test_filter.py",
    "content": "import random\nimport tempfile\n\nimport numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\nfrom annlite import AnnLite\nfrom annlite.filter import Filter\n\n\ndef test_empty_filter():\n    f = Filter()\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == ''\n    assert parameters == ()\n\n\ndef test_simple_filter():\n    f = Filter({'brand': {'$lt': 1}})\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == '(brand < ?)'\n    assert parameters == (1,)\n\n\ndef test_logic_operator():\n    f = Filter({'$and': {'brand': {'$lt': 1}, 'price': {'$gte': 50}}})\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == '(brand < ?) AND (price >= ?)'\n    assert parameters == (1, 50)\n\n    f = Filter({'brand': {'$lt': 1}, 'price': {'$gte': 50}})\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == '(brand < ?) AND (price >= ?)'\n    assert parameters == (1, 50)\n\n    f = Filter({'$or': {'brand': {'$lt': 1}, 'price': {'$gte': 50}}})\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == '(brand < ?) OR (price >= ?)'\n    assert parameters == (1, 50)\n\n\ndef test_membership_operator():\n    f = Filter({'$and': {'brand': {'$in': ['Nike', 'Gucci']}, 'price': {'$gte': 50}}})\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == '(brand IN(?, ?)) AND (price >= ?)'\n    assert parameters == ('Nike', 'Gucci', 50)\n\n    f = Filter({'$or': {'brand': {'$nin': ['Nike', 'Gucci']}, 'price': {'$gte': 50}}})\n    where_clause, parameters = f.parse_where_clause()\n    assert where_clause == '(brand NOT IN(?, ?)) OR (price >= ?)'\n    assert parameters == ('Nike', 'Gucci', 50)\n\n\ndef test_cases():\n    express = {\n        '$and': {\n            'price': {'$gte': 0, '$lte': 54},\n            'rating': {'$gte': 1},\n            'year': {'$gte': 2007, '$lte': 2010},\n        }\n    }\n    f = Filter(express)\n    where_clause, parameters = f.parse_where_clause()\n    assert (\n        where_clause\n        == '(price >= ?) AND (price <= ?) AND (rating >= ?) AND (year >= ?) AND (year <= ?)'\n    )\n    assert parameters == (0, 54, 1, 2007, 2010)\n\n    express = {\n        '$and': {\n            'price': {'$or': [{'price': {'$gte': 0}}, {'price': {'$lte': 54}}]},\n            'rating': {'$gte': 1},\n            'year': {'$gte': 2007, '$lte': 2010},\n        }\n    }\n    f = Filter(express)\n\n    where_clause, parameters = f.parse_where_clause()\n    assert (\n        where_clause\n        == '((price >= ?) OR (price <= ?)) AND (rating >= ?) AND (year >= ?) AND (year <= ?)'\n    )\n    assert parameters == (0, 54, 1, 2007, 2010)\n\n    express = {\n        '$and': {\n            '$or': [{'price': {'$gte': 0}}, {'price': {'$lte': 54}}],\n            'rating': {'$gte': 1},\n            'year': {'$gte': 2007, '$lte': 2010},\n        }\n    }\n    f = Filter(express)\n\n    where_clause, parameters = f.parse_where_clause()\n    assert (\n        where_clause\n        == '((price >= ?) OR (price <= ?)) AND (rating >= ?) AND (year >= ?) AND (year <= ?)'\n    )\n    assert parameters == (0, 54, 1, 2007, 2010)\n\n\ndef test_error_filter():\n    f = Filter({'$may': {'brand': {'$lt': 1}, 'price': {'$gte': 50}}})\n    with pytest.raises(ValueError):\n        f.parse_where_clause()\n\n\n@pytest.mark.parametrize(\n    'columns', [[('x', float)], [('x', 'float')], {'x': 'float'}, {'x': float}]\n)\ndef test_filter_with_columns(tmpfile, columns):\n    N = 100\n    D = 2\n    limit = 3\n\n    index = AnnLite(D, columns=columns, data_path=tmpfile, include_metadata=True)\n    X = np.random.random((N, D)).astype(np.float32)\n\n    docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=X[i], tags={'x': random.random()})\n            for i in range(N)\n        ]\n    )\n    index.index(docs)\n\n    matches = index.filter(\n        filter={'x': {'$lt': 0.5}}, limit=limit, include_metadata=True\n    )\n    assert len(matches) == limit\n    for m in matches:\n        assert m.tags['x'] < 0.5\n\n\n@pytest.mark.parametrize('filterable_attrs', [{'x': 'float'}, {'x': float}])\ndef test_filter_with_dict(tmpfile, filterable_attrs):\n    N = 100\n    D = 2\n    limit = 3\n\n    index = AnnLite(\n        D,\n        filterable_attrs=filterable_attrs,\n        data_path=tmpfile,\n        include_metadata=True,\n    )\n    X = np.random.random((N, D)).astype(np.float32)\n\n    docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=X[i], tags={'x': random.random()})\n            for i in range(N)\n        ]\n    )\n    index.index(docs)\n\n    matches = index.filter(\n        filter={'x': {'$lt': 0.5}}, limit=limit, include_metadata=True\n    )\n    assert len(matches) == limit\n    for m in matches:\n        assert m.tags['x'] < 0.5\n\n\n@pytest.mark.parametrize('limit', [1, 5])\n@pytest.mark.parametrize('offset', [1, 5])\n@pytest.mark.parametrize('order_by', ['x', 'y'])\n@pytest.mark.parametrize('ascending', [True, False])\ndef test_filter_with_limit_offset(tmpfile, limit, offset, order_by, ascending):\n    N = 100\n    D = 2\n\n    index = AnnLite(\n        D,\n        filterable_attrs={'x': 'float', 'y': 'float'},\n        data_path=tmpfile,\n        include_metadata=True,\n    )\n    X = np.random.random((N, D)).astype(np.float32)\n\n    docs = DocumentArray(\n        [\n            Document(\n                id=f'{i}',\n                embedding=X[i],\n                tags={'x': random.random(), 'y': random.random()},\n            )\n            for i in range(N)\n        ]\n    )\n    index.index(docs)\n\n    matches = index.filter(\n        filter={'x': {'$lt': 0.5}},\n        limit=limit,\n        offset=offset,\n        order_by=order_by,\n        ascending=ascending,\n        include_metadata=True,\n    )\n    assert len(matches) == limit\n\n    for i in range(len(matches) - 1):\n        m = matches[i]\n        assert m.tags['x'] < 0.5\n        if ascending:\n            assert m.tags[order_by] <= matches[i + 1].tags[order_by]\n        else:\n            assert m.tags[order_by] >= matches[i + 1].tags[order_by]\n\n\n@pytest.mark.parametrize('limit', [1, 5])\ndef test_filter_with_wrong_columns(tmpfile, limit):\n    N = 100\n    D = 128\n\n    index = AnnLite(\n        D,\n        columns=[('price', float)],\n        data_path=tmpfile,\n    )\n    X = np.random.random((N, D)).astype(np.float32)\n\n    docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=X[i], tags={'price': random.random()})\n            for i in range(N)\n        ]\n    )\n\n    index.index(docs)\n\n    matches = index.filter(\n        filter={'price': {'$lte': 50}},\n        limit=limit,\n        include_metadata=True,\n    )\n\n    assert len(matches) == limit\n\n    import sqlite3\n\n    with pytest.raises(sqlite3.OperationalError):\n        matches = index.filter(\n            filter={'price_': {'$lte': 50}},\n            include_metadata=True,\n        )\n\n    matches = index.filter(\n        filter={'price': {'$lte': 50}},\n        limit=limit,\n        include_metadata=True,\n    )\n\n    assert len(matches) == limit\n"
  },
  {
    "path": "tests/test_hnsw_load_save.py",
    "content": "import os\n\nimport numpy as np\nimport pytest\n\nfrom annlite.core.index.hnsw import HnswIndex\n\nn_examples = 100\nn_features = 10\n\n\n@pytest.fixture\ndef build_data():\n    Xt = np.random.random((n_examples, n_features)).astype(np.float32)\n    return Xt\n\n\n@pytest.fixture\ndef build_hnsw(build_data):\n    Xt = build_data\n    hnsw = HnswIndex(dim=n_features)\n    hnsw.add_with_ids(x=Xt, ids=list(range(len(Xt))))\n    return hnsw\n\n\ndef test_save_and_load(tmpdir, build_hnsw):\n    hnsw = build_hnsw\n    hnsw.dump(os.path.join(tmpdir, 'hnsw.pkl'))\n    assert os.path.exists(os.path.join(tmpdir, 'hnsw.pkl')) is True\n\n    hnsw_ = HnswIndex(dim=n_features, index_file=os.path.join(tmpdir, 'hnsw.pkl'))\n    assert hnsw_.size == hnsw.size\n\n\ndef test_loading_from_wrong_path(tmpfile):\n    with pytest.raises(FileNotFoundError):\n        HnswIndex(dim=n_features, index_file=os.path.join(tmpfile, 'hnsw_wrong.pkl'))\n"
  },
  {
    "path": "tests/test_index.py",
    "content": "import operator\nimport os\nimport random\nimport uuid\nfrom unittest.mock import patch\n\nimport hubble\nimport numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\nfrom annlite import AnnLite\n\nN = 1000  # number of data points\nNq = 5\nNt = 2000\nD = 128  # dimensionality / number of features\n# pq params below -----------\nn_clusters = 256\nn_subvectors = 8\nd_subvector = int(D / n_subvectors)\n\nnumeric_operators = {\n    '$gte': operator.ge,\n    '$gt': operator.gt,\n    '$lte': operator.le,\n    '$lt': operator.lt,\n    '$eq': operator.eq,\n    '$neq': operator.ne,\n}\n\ncategorical_operators = {'$eq': operator.eq, '$neq': operator.ne}\n\ntoken = 'ed17d158d95d3f53f60eed445d783c80'\n\n\n@pytest.fixture\ndef annlite_index(tmpfile):\n    Xt = np.random.random((Nt, D)).astype(\n        np.float32\n    )  # 2,000 128-dim vectors for training\n\n    index = AnnLite(n_dim=D, data_path=tmpfile)\n    return index\n\n\n@pytest.fixture\ndef annlite_with_data(tmpfile):\n    columns = [('x', float)]\n    index = AnnLite(n_dim=D, columns=columns, data_path=tmpfile)\n\n    X = np.random.random((N, D)).astype(\n        np.float32\n    )  # 10,000 128-dim vectors to be indexed\n\n    docs = DocumentArray(\n        [\n            Document(id=f'{i}', embedding=X[i], tags={'x': random.random()})\n            for i in range(N)\n        ]\n    )\n    index.index(docs)\n    return index\n\n\n@pytest.fixture\ndef heterogenenous_da(tmpfile):\n    prices = [10.0, 25.0, 50.0, 100.0]\n    categories = ['comics', 'movies', 'audiobook']\n\n    columns = [('price', float), ('category', str)]\n    index = AnnLite(n_dim=D, columns=columns, data_path=tmpfile)\n\n    X = np.random.random((N, D)).astype(\n        np.float32\n    )  # 10,000 128-dim vectors to be indexed\n    docs = [\n        Document(\n            id=f'{i}',\n            embedding=X[i],\n            tags={\n                'price': np.random.choice(prices),\n                'category': np.random.choice(categories),\n            },\n        )\n        for i in range(N)\n    ]\n    da = DocumentArray(docs)\n\n    return da\n\n\n@pytest.fixture\ndef annlite_with_heterogeneous_tags(tmpfile, heterogenenous_da):\n    columns = [('price', float), ('category', str)]\n    index = AnnLite(n_dim=D, columns=columns, data_path=tmpfile)\n    index.index(heterogenenous_da)\n    return index\n\n\ndef test_index(annlite_index):\n    X = np.random.random((N, D)).astype(\n        np.float32\n    )  # 10,000 128-dim vectors to be indexed\n\n    docs = DocumentArray([Document(id=f'{i}', embedding=X[i]) for i in range(N)])\n    annlite_index.index(docs)\n\n\n@pytest.mark.parametrize('dtype', [np.int64, np.float32, np.float64])\ndef test_dtype(annlite_index, dtype):\n    X = np.random.random((N, D)).astype(dtype)  # 10,000 128-dim vectors to be indexed\n\n    docs = DocumentArray([Document(id=f'{i}', embedding=X[i]) for i in range(N)])\n    annlite_index.index(docs)\n\n\ndef test_delete(annlite_with_data):\n    annlite_with_data.delete(['0', '1'])\n\n\ndef test_update(annlite_with_data):\n    X = np.random.random((5, D)).astype(np.float32)  # 5 128-dim vectors to be indexed\n    docs = DocumentArray([Document(id=f'{i}', embedding=X[i]) for i in range(5)])\n    annlite_with_data.update(docs)\n\n\ndef test_query(annlite_with_data):\n    X = np.random.random((Nq, D)).astype(np.float32)  # a 128-dim query vector\n    query = DocumentArray([Document(embedding=X[i]) for i in range(5)])\n\n    annlite_with_data.search(query)\n\n    for i in range(len(query[0].matches) - 1):\n        assert (\n            query[0].matches[i].scores['euclidean'].value\n            <= query[0].matches[i + 1].scores['euclidean'].value\n        )\n\n\ndef test_index_query_with_filtering_sorted_results(annlite_with_data):\n    X = np.random.random((Nq, D)).astype(np.float32)\n    query = DocumentArray([Document(embedding=X[i]) for i in range(5)])\n    annlite_with_data.search(query, filter={'x': {'$gt': 0.6}}, include_metadata=True)\n\n    for i in range(len(query[0].matches) - 1):\n        assert (\n            query[0].matches[i].scores['euclidean'].value\n            <= query[0].matches[i + 1].scores['euclidean'].value\n        )\n        assert query[0].matches[i].tags['x'] > 0.6\n\n\n@pytest.mark.parametrize('operator', list(numeric_operators.keys()))\ndef test_query_search_filter_float_type(annlite_with_heterogeneous_tags, operator):\n    X = np.random.random((Nq, D)).astype(np.float32)\n    query_da = DocumentArray([Document(embedding=X[i]) for i in range(Nq)])\n\n    thresholds = [20, 50, 100, 400]\n\n    for threshold in thresholds:\n        annlite_with_heterogeneous_tags.search(\n            query_da, filter={'price': {operator: threshold}}, include_metadata=True\n        )\n        for query in query_da:\n            assert all(\n                [\n                    numeric_operators[operator](m.tags['price'], threshold)\n                    for m in query.matches\n                ]\n            )\n\n\n@pytest.mark.parametrize('operator', list(numeric_operators.keys()))\ndef test_query_search_numpy_filter_float_type(\n    annlite_with_heterogeneous_tags, heterogenenous_da, operator\n):\n    X = np.random.random((Nq, D)).astype(np.float32)\n    query_np = np.array([X[i] for i in range(Nq)])\n    da = heterogenenous_da\n    thresholds = [20, 50, 100, 400]\n\n    for threshold in thresholds:\n        dists, doc_ids = annlite_with_heterogeneous_tags.search_numpy(\n            query_np, filter={'price': {operator: threshold}}, include_metadata=True\n        )\n        for doc_ids_query_k in doc_ids:\n            assert all(\n                [\n                    numeric_operators[operator](\n                        da[int(doc_id)].tags['price'], threshold\n                    )\n                    for doc_id in doc_ids_query_k\n                ]\n            )\n\n\n@pytest.mark.parametrize('operator', list(categorical_operators.keys()))\ndef test_search_filter_str(annlite_with_heterogeneous_tags, operator):\n    X = np.random.random((Nq, D)).astype(np.float32)\n    query_da = DocumentArray([Document(embedding=X[i]) for i in range(Nq)])\n\n    categories = ['comics', 'movies', 'audiobook']\n    for category in categories:\n        annlite_with_heterogeneous_tags.search(\n            query_da, filter={'category': {operator: category}}, include_metadata=True\n        )\n        for query in query_da:\n            assert all(\n                [\n                    numeric_operators[operator](m.tags['category'], category)\n                    for m in query.matches\n                ]\n            )\n\n\n@pytest.mark.parametrize('operator', list(categorical_operators.keys()))\ndef test_search_numpy_filter_str(\n    annlite_with_heterogeneous_tags, heterogenenous_da, operator\n):\n    X = np.random.random((Nq, D)).astype(np.float32)\n    query_np = np.array([X[i] for i in range(Nq)])\n    da = heterogenenous_da\n\n    categories = ['comics', 'movies', 'audiobook']\n    for category in categories:\n        dists, doc_ids = annlite_with_heterogeneous_tags.search_numpy(\n            query_np, filter={'category': {operator: category}}, include_metadata=True\n        )\n        for doc_ids_query_k in doc_ids:\n            assert all(\n                [\n                    numeric_operators[operator](\n                        da[int(doc_id)].tags['category'], category\n                    )\n                    for doc_id in doc_ids_query_k\n                ]\n            )\n\n\ndef test_search_numpy_membership_filter(\n    annlite_with_heterogeneous_tags, heterogenenous_da\n):\n    X = np.random.random((Nq, D)).astype(np.float32)\n    query_np = np.array([X[i] for i in range(Nq)])\n    da = heterogenenous_da\n\n    dists, doc_ids = annlite_with_heterogeneous_tags.search_numpy(\n        query_np,\n        filter={'category': {'$in': ['comics', 'audiobook']}},\n        include_metadata=True,\n    )\n    for doc_ids_query_k in doc_ids:\n        assert len(doc_ids)\n        assert all(\n            [\n                da[int(doc_id)].tags['category'] in ['comics', 'audiobook']\n                for doc_id in doc_ids_query_k\n            ]\n        )\n\n    dists, doc_ids = annlite_with_heterogeneous_tags.search_numpy(\n        query_np,\n        filter={'category': {'$nin': ['comics', 'audiobook']}},\n        include_metadata=True,\n    )\n    for doc_ids_query_k in doc_ids:\n        assert len(doc_ids)\n        assert all(\n            [\n                da[int(doc_id)].tags['category'] not in ['comics', 'audiobook']\n                for doc_id in doc_ids_query_k\n            ]\n        )\n\n\ndef delete_artifact(tmpname):\n    client = hubble.Client(token=token, max_retries=None, jsonify=True)\n    art_list = client.list_artifacts(filter={'metaData.name': tmpname})\n    for art in art_list['data']:\n        client.delete_artifact(id=art['_id'])\n\n\ndef test_local_backup_restore(tmpdir):\n    X = np.random.random((N, D))\n    docs = DocumentArray([Document(id=f'{i}', embedding=X[i]) for i in range(N)])\n    index = AnnLite(n_dim=D, data_path=tmpdir / 'workspace' / '0')\n    index.index(docs)\n\n    tmpname = uuid.uuid4().hex\n    index.backup()\n    index.close()\n\n    index = AnnLite(n_dim=D, data_path=tmpdir / 'workspace' / '0')\n    index.restore()\n\n    status = index.stat\n    assert int(status['total_docs']) == N\n    assert int(status['index_size']) == N\n\n\n@pytest.mark.skip(reason='This test requires a running hubble instance')\ndef test_remote_backup_restore(tmpdir):\n    X = np.random.random((N, D))\n    docs = DocumentArray([Document(id=f'{i}', embedding=X[i]) for i in range(N)])\n    index = AnnLite(n_dim=D, data_path=tmpdir / 'workspace' / '0')\n    index.index(docs)\n\n    tmpname = uuid.uuid4().hex\n    index.backup(target_name='test_remote_backup_restore', token=token)\n\n    index = AnnLite(n_dim=D, data_path=tmpdir / 'workspace' / '0')\n    index.restore(source_name='test_remote_backup_restore', token=token)\n\n    delete_artifact(tmpname)\n    status = index.stat\n    assert int(status['total_docs']) == N\n    assert int(status['index_size']) == N\n"
  },
  {
    "path": "tests/test_pq_bind.py",
    "content": "import numpy as np\nimport pytest\n\nfrom annlite.core.codec.pq import PQCodec\nfrom annlite.pq_bind import dist_pqcodes_to_codebooks, precompute_adc_table\n\nn_examples = 2000\nn_features = 128\nn_queries = 5\nn_cells = 10\nn_clusters = 256\nn_subvectors = 32\nd_subvector = int(n_features / n_subvectors)\ntop_k = 100\n\n\n@pytest.fixture\ndef build_data():\n    Xt = np.random.random((n_examples, n_features)).astype(np.float32)\n    return Xt\n\n\n@pytest.fixture\ndef build_pq_codec(build_data):\n    Xt = build_data\n    pq_codec = PQCodec(dim=n_features, n_subvectors=n_subvectors, n_clusters=n_clusters)\n    pq_codec.fit(Xt)\n    return pq_codec\n\n\ndef test_pq_adc_table_shape(build_pq_codec):\n    pq_codec = build_pq_codec\n    assert pq_codec.codebooks.shape == (n_subvectors, n_clusters, d_subvector)\n\n\ndef test_pq_adc_table_computation(build_data):\n    def numpy_adc_table(query, n_subvectors, n_clusters, d_subvector, codebooks):\n        dtable = np.empty((n_subvectors, n_clusters), dtype=np.float32)\n        for m in range(n_subvectors):\n            query_sub = query[m * d_subvector : (m + 1) * d_subvector]\n            dtable[m, :] = np.linalg.norm(codebooks[m] - query_sub, axis=1) ** 2\n\n        return dtable\n\n    query = build_data[0]\n    codebooks = np.random.random((n_subvectors, n_clusters, d_subvector)).astype(\n        np.float32\n    )\n\n    np_distance_table = numpy_adc_table(\n        query, n_subvectors, n_clusters, d_subvector, codebooks\n    )\n\n    distance_table_cy = precompute_adc_table(query, d_subvector, n_clusters, codebooks)\n\n    np_distance_table_cy = np.asarray(distance_table_cy)\n    np.testing.assert_array_almost_equal(\n        np_distance_table, np_distance_table_cy, decimal=5\n    )\n\n\ndef test_pq_adc_table_computation_interface(build_pq_codec, build_data):\n    pq_codec = build_pq_codec\n    query = build_data[0]\n\n    np_distance_table = pq_codec.precompute_adc(query).dtable\n    distance_table_cy = precompute_adc_table(\n        query, pq_codec.d_subvector, pq_codec.n_clusters, pq_codec.codebooks\n    )\n\n    np_distance_table_cy = np.asarray(distance_table_cy)\n\n    np.testing.assert_array_almost_equal(\n        np_distance_table, np_distance_table_cy, decimal=5\n    )\n"
  },
  {
    "path": "tests/test_pq_index.py",
    "content": "from time import time\n\nimport numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\nfrom loguru import logger\n\nfrom annlite import AnnLite, pq_bind\nfrom annlite.core.codec.pq import PQCodec\nfrom annlite.core.index.pq_index import PQIndex\nfrom annlite.math import euclidean, l2_normalize\n\nN = 1000  # number of data points\nNq = 5\nNt = 2000\nD = 64  # dimensionality / number of features\n\n\n@pytest.fixture\ndef random_docs():\n    X = np.random.random((N, D)).astype(\n        np.float32\n    )  # 10,000 64-dim vectors to be indexed\n    docs = DocumentArray(\n        [Document(id=f'{i}', embedding=X[i], tags={'x': str(i)}) for i in range(N)]\n    )\n    return docs\n\n\ndef test_pq_index_dist_mat(random_docs):\n    X = random_docs.embeddings\n    N, D = X.shape\n    pq_class = PQCodec(dim=D)\n    pq_class.fit(X)\n\n    test_cases = X[:10]\n    # -------------- true\n    pq_bind_re = []\n    for i in range(len(test_cases)):\n        query = test_cases[i]\n        dtable = pq_bind.precompute_adc_table(\n            query, pq_class.d_subvector, pq_class.n_clusters, pq_class.codebooks\n        )\n        pq_bind_re.append(dtable)\n    pq_bind_re = np.stack(pq_bind_re)\n    # -------------- test\n    pq_dist_mat = pq_class.get_dist_mat(test_cases)\n\n    assert np.allclose(pq_bind_re, pq_dist_mat)\n\n\ndef test_hnsw_pq_load_empty(tmpfile, random_docs):\n    pq_index = AnnLite(\n        D,\n        data_path=tmpfile,\n        n_subvectors=8,\n    )\n\n    with pytest.raises(RuntimeError):\n        # unable to add or search before a actual training of PQ\n        pq_index.index(random_docs)\n\n    with pytest.raises(RuntimeError):\n        # unable to add or search before a actual training of PQ\n        pq_index.search(random_docs)\n\n\ndef test_hnsw_pq_load(tmpfile, random_docs):\n    pq_index = AnnLite(\n        D,\n        data_path=tmpfile,\n        n_subvectors=8,\n    )\n    pq_index.train(random_docs.embeddings)\n    pq_index.index(random_docs)\n    assert all([x.pq_codec.is_trained for x in pq_index._vec_indexes])\n    assert all([x._index.pq_enable for x in pq_index._vec_indexes])\n\n\n@pytest.mark.parametrize('n_clusters', [256, 512, 768])\ndef test_hnsw_pq_search_multi_clusters(tmpdir, n_clusters, random_docs):\n    total_test = 10\n    topk = 50\n\n    X = random_docs.embeddings\n    N, Dim = X.shape\n    computed_dist = euclidean(X, X)\n    computed_labels = np.argsort(computed_dist, axis=1)[:, :topk]\n\n    query = DocumentArray([Document(embedding=X[i]) for i in range(total_test)])\n    test_query = DocumentArray([Document(embedding=X[i]) for i in range(total_test)])\n\n    # HNSW search with float----------------------------------\n    no_pq_index = AnnLite(D, data_path=tmpdir / 'no_pq_index', metric='EUCLIDEAN')\n\n    hnsw_s = time()\n    no_pq_index.index(random_docs)\n    hnsw_d = time() - hnsw_s\n    print('Index time', hnsw_d)\n    hnsw_s = time()\n    no_pq_index.search(query, limit=topk)\n    hnsw_d = time() - hnsw_s\n    print(hnsw_d)\n    no_pq_index.close()\n    # HNSW search with quantization---------------------------\n    pq_index = AnnLite(\n        D,\n        data_path=tmpdir / 'pq_index',\n        n_subvectors=8,\n        n_clusters=n_clusters,\n        metric='EUCLIDEAN',\n    )\n    pq_index.train(X)\n    hnsw_pq_s = time()\n    pq_index.index(random_docs)\n    hnsw_pq_d = time() - hnsw_pq_s\n    print('Index time', hnsw_pq_d)\n\n    hnsw_pq_s = time()\n    pq_index.search(test_query, limit=topk)\n    hnsw_pq_d = time() - hnsw_pq_s\n    # ----------------------------------\n\n    # PQ linear search----------------------------------\n    _pq_codec = pq_index._pq_codec\n    ids = np.array([int(doc.id) for doc in random_docs])\n    linear_pq_index = PQIndex(Dim, _pq_codec)\n    linear_pq_index.add_with_ids(X, ids)\n\n    search_x = test_query.embeddings\n    pq_dists = []\n    linear_results = []\n    pq_s = time()\n    for i in range(total_test):\n        pq_dist, linear_result = linear_pq_index.search(search_x[i], limit=topk)\n        pq_dists.append(pq_dist)\n        linear_results.append(linear_result)\n    pq_d = time() - pq_s\n    # ----------------------------------\n\n    precision = []\n    original_precision = []\n    pq_precision = []\n    for i in range(total_test):\n        real_ground_truth = set([str(i) for i in computed_labels[i]])\n        ground_truth = set([m.id for m in query[i].matches])\n        pq_result = set([m.id for m in test_query[i].matches])\n        linear_pq_result = set([str(i_id) for i_id in linear_results[i]])\n        original_precision.append(len(real_ground_truth & ground_truth) / topk)\n        pq_precision.append(len(real_ground_truth & linear_pq_result) / topk)\n        precision.append(len(real_ground_truth & pq_result) / topk)\n    logger.info(f'Total test {total_test}')\n    logger.info(\n        f'PQ backend top-{topk} precision: {np.mean(pq_precision)}, time {pq_d}'\n    )\n    logger.info(\n        f'HNSW backend top-{topk} precision: {np.mean(original_precision)}, time {hnsw_d}'\n    )\n    logger.info(\n        f'HNSW PQ backend top-{topk} precision: {np.mean(precision)}, time {hnsw_pq_d}'\n    )\n    # TODO: fix the precision issue\n    # assert np.mean(precision) > 0.9\n"
  },
  {
    "path": "tests/test_projector.py",
    "content": "import numpy as np\nimport pytest\n\nfrom annlite.core.codec.projector import ProjectorCodec\n\nn_examples = 1000\nn_features = 512\nn_components = 128\nbatch_size = 200\n\n\n@pytest.fixture\ndef build_data():\n    Xt = np.random.random((n_examples, n_features)).astype(np.float32)\n    return Xt\n\n\n@pytest.fixture\ndef build_projector(build_data):\n    Xt = build_data\n\n    projector_list = []\n    for is_incremental in [True, False]:\n        projector = ProjectorCodec(\n            dim=n_features,\n            n_components=n_components,\n        )\n        if is_incremental:\n            for i in range(0, len(Xt), batch_size):\n                projector.partial_fit(Xt[i : i + batch_size])\n                i += batch_size\n        else:\n            projector.fit(Xt)\n        projector_list.append(projector)\n    return projector_list\n\n\ndef test_encode_decode(build_data, build_projector):\n    Xt = build_data\n\n    projector_list = build_projector\n\n    for projector in projector_list:\n        transformed_vecs = projector.encode(Xt)\n        assert transformed_vecs.shape == (Xt.shape[0], n_components)\n\n        original_vecs = projector.decode(transformed_vecs)\n        assert original_vecs.shape == Xt.shape\n\n\ndef test_save_and_load(tmpdir, build_data, build_projector):\n    import os\n    from pathlib import Path\n\n    Xt = build_data\n    projector_list = build_projector\n\n    for projector in projector_list:\n        projector.dump(Path(os.path.join(tmpdir, 'projector.pkl')))\n        assert os.path.exists(os.path.join(tmpdir, 'projector.pkl')) is True\n\n        projector_ = ProjectorCodec.load(Path(os.path.join(tmpdir, 'projector.pkl')))\n        assert projector.components.shape == projector_.components.shape\n\n        before = projector.encode(Xt)\n        after = projector_.encode(Xt)\n        np.testing.assert_array_almost_equal(before, after, decimal=5)\n"
  },
  {
    "path": "tests/test_projector_index.py",
    "content": "import numpy as np\nimport pytest\nfrom docarray import Document, DocumentArray\n\nfrom annlite.index import AnnLite\n\nn_examples = 1000\nn_features = 512\nn_components = 128\nbatch_size = 200\n\n\n@pytest.fixture\ndef build_data():\n    Xt = np.random.random((n_examples, n_features)).astype(np.float32)\n    return Xt\n\n\n@pytest.fixture\ndef build_projector_annlite(tmpfile):\n    index = AnnLite(n_dim=n_features, data_path=tmpfile)\n    return index\n\n\n@pytest.fixture\ndef projector_annlite_with_data(build_data, build_projector_annlite):\n    Xt = build_data\n    indexer = build_projector_annlite\n\n    docs = DocumentArray(\n        [Document(id=f'{i}', embedding=Xt[i]) for i in range(n_examples)]\n    )\n    indexer.index(docs)\n    return indexer\n\n\ndef test_delete(projector_annlite_with_data):\n    indexer = projector_annlite_with_data\n    indexer.delete(['0', '1'])\n\n\ndef test_update(projector_annlite_with_data):\n    indexer = projector_annlite_with_data\n    X = np.random.random((5, n_features)).astype(np.float32)\n    docs = DocumentArray([Document(id=f'{i}', embedding=X[i]) for i in range(5)])\n    indexer.update(docs)\n\n\ndef test_query(projector_annlite_with_data):\n    indexer = projector_annlite_with_data\n    X = np.random.random((5, n_features)).astype(np.float32)  # a 128-dim query vector\n    query = DocumentArray([Document(embedding=X[i]) for i in range(5)])\n\n    indexer.search(query)\n\n    for i in range(len(query[0].matches) - 1):\n        assert (\n            query[0].matches[i].scores['euclidean'].value\n            <= query[0].matches[i + 1].scores['euclidean'].value\n        )\n"
  },
  {
    "path": "tests/test_store.py",
    "content": "import pytest\n\nfrom annlite.storage.kv import DocStorage\n\n\ndef test_get(tmpfile, docs):\n    storage = DocStorage(tmpfile)\n\n    storage.insert(docs)\n\n    doc = storage.get('doc1')[0]\n    assert doc.id == 'doc1'\n    assert (doc.embedding == [1, 0, 0, 0]).all()\n\n    docs = storage.get('doc7')\n    assert len(docs) == 0\n\n\ndef test_update(tmpfile, docs, update_docs):\n    storage = DocStorage(tmpfile)\n    storage.insert(docs)\n\n    storage.update(update_docs)\n\n    doc = storage.get('doc1')[0]\n    assert (doc.embedding == [0, 0, 0, 1]).all()\n\n\ndef test_delete(tmpfile, docs):\n    storage = DocStorage(tmpfile)\n    storage.insert(docs)\n    storage.delete(['doc1'])\n    docs = storage.get('doc1')\n    assert len(docs) == 0\n\n\ndef test_clear(tmpfile, docs):\n    storage = DocStorage(tmpfile)\n    storage.insert(docs)\n\n    assert storage.size == 6\n    storage.clear()\n    assert storage.size == 0\n\n\ndef test_batched_iterator(tmpfile, docs):\n    storage = DocStorage(tmpfile)\n    storage.insert(docs)\n    for docs in storage.batched_iterator(batch_size=3):\n        assert len(docs) == 3\n\n\n@pytest.mark.parametrize('protocol', ['pickle', 'protobuf'])\ndef test_searalize(tmpfile, protocol, docs):\n    storage = DocStorage(tmpfile, serialize_config={'protocol': protocol})\n    storage.insert(docs)\n\n    doc = storage.get('doc1')[0]\n    assert doc.id == 'doc1'\n    assert (doc.embedding == [1, 0, 0, 0]).all()\n\n    docs = storage.get('doc7')\n    assert len(docs) == 0\n"
  },
  {
    "path": "tests/test_table.py",
    "content": "import pytest\nfrom docarray import Document, DocumentArray\n\nfrom annlite.storage.table import CellTable, MetaTable\n\n\n@pytest.fixture\ndef dummy_cell_table():\n    table = CellTable(name='dummy', in_memory=True, lazy_create=True)\n    table.add_column('name', 'TEXT', create_index=True)\n    table.add_column('price', 'FLOAT', create_index=True)\n    table.add_column('category', 'TEXT')\n    table.create_table()\n\n    return table\n\n\n@pytest.fixture\ndef sample_docs():\n    return DocumentArray(\n        [\n            Document(\n                id='0', tags={'name': 'orange', 'price': 1.2, 'category': 'fruit'}\n            ),\n            Document(id='1', tags={'name': 'banana', 'price': 2, 'category': 'fruit'}),\n            Document(id='2', tags={'name': 'poly', 'price': 5.1, 'category': 'animal'}),\n            Document(id='3', tags={'name': 'bread'}),\n        ]\n    )\n\n\n@pytest.fixture\ndef table_with_data(dummy_cell_table, sample_docs):\n    dummy_cell_table.insert(sample_docs)\n    return dummy_cell_table\n\n\ndef test_create_cell_table():\n    table = CellTable(name='cell_table_x', lazy_create=True)\n    table.add_column('x', 'float')\n    table.create_table()\n    assert table.existed()\n\n\ndef test_schema(dummy_cell_table):\n    schema = dummy_cell_table.schema\n    assert len(schema.split('\\n')) == 5\n\n\ndef test_query(table_with_data):\n    result = table_with_data.query(\n        where_clause='(category = ?) AND (price < ?)', where_params=('fruit', 3)\n    )\n\n    assert len(result) == 2\n    assert result[0] == 0\n\n\ndef test_get_docid_by_offset(table_with_data):\n    doc_id = table_with_data.get_docid_by_offset(0)\n    assert doc_id == '0'\n\n    doc_id = table_with_data.get_docid_by_offset(4)\n    assert doc_id is None\n\n\ndef test_exist(table_with_data):\n    assert table_with_data.exist('3')\n\n\ndef test_delete(table_with_data):\n    table_with_data.delete(['3'])\n\n    assert not table_with_data.exist('3')\n\n    table_with_data.delete_by_offset(2)\n    assert not table_with_data.exist('2')\n\n\ndef test_count(table_with_data):\n    count = table_with_data.count(\n        where_clause='(category = ?) AND (price > ?)', where_params=('fruit', 5)\n    )\n    assert count == 0\n\n    count = table_with_data.count(\n        where_clause='(category = ?) AND (price > ?) and (price < ?)',\n        where_params=('fruit', 1, 1.5),\n    )\n    assert count == 1\n\n    count = table_with_data.count(\n        where_clause='(category = ?) AND (price < ?)', where_params=('fruit', 3)\n    )\n    assert count == 2\n\n    count = table_with_data.count(\n        where_clause='(category IN (?, ?)) AND (price < ?)',\n        where_params=('fruit', 'animal', 3),\n    )\n    assert count == 2\n\n    count = table_with_data.count(\n        where_clause='(category IN (?)) AND (price < ?)', where_params=('fruit', 1)\n    )\n    assert count == 0\n\n\ndef test_create_meta_table(tmpdir):\n    import datetime\n\n    table = MetaTable('meta_test', data_path=tmpdir)\n    addr = table.get_latest_commit()\n    assert addr is None\n\n    table.add_address('0', 0, 1)\n    table.add_address('2', 1, 5)\n\n    time_since = datetime.datetime.utcnow()\n    table.add_address('0', 1, 2)\n\n    assert table.get_address('0') == (1, 2)\n    assert table.get_address('2') == (1, 5)\n\n    assert len(list(table.iter_addresses())) == 2\n    addresses = list(table.iter_addresses(time_since=time_since))\n    assert addresses == [('0', 1, 2)]\n\n    addr = table.get_latest_commit()\n    assert addr[:3] == ('0', 1, 2)\n    assert addr[-1] >= time_since\n\n    time_since = datetime.datetime.utcnow()\n    table.delete_address('0')\n    addresses = list(table.iter_addresses(time_since=time_since))\n    assert addresses == []\n"
  }
]